UNPKG

dotnet-deps-parser

Version:

Generate a dep tree given a collection of manifests

325 lines 15.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getTargetFrameworksFromProjectAssetsJson = exports.getTargetFrameworksFromProjectJson = exports.getTargetFrameworksFromProjectConfig = exports.getSdkFromProjectFile = exports.getTargetFrameworksFromProjectFile = exports.getPropertiesMap = exports.parseXmlFile = exports.getDependenciesFromPackageReference = exports.getDependencyTreeFromProjectFile = exports.getDependencyTreeFromPackagesConfig = exports.getDependencyTreeFromProjectJson = exports.ProjectJsonDepType = exports.DepType = void 0; const parseXML = require("xml2js"); const lodash_1 = require("lodash"); const error_catalog_nodejs_public_1 = require("@snyk/error-catalog-nodejs-public"); var DepType; (function (DepType) { DepType["prod"] = "prod"; DepType["dev"] = "dev"; })(DepType || (exports.DepType = DepType = {})); var ProjectJsonDepType; (function (ProjectJsonDepType) { ProjectJsonDepType["build"] = "build"; ProjectJsonDepType["project"] = "project"; ProjectJsonDepType["platform"] = "platform"; ProjectJsonDepType["default"] = "default"; })(ProjectJsonDepType || (exports.ProjectJsonDepType = ProjectJsonDepType = {})); function getDependencyTreeFromProjectJson(manifestFile, includeDev = false) { const depTree = { dependencies: {}, hasDevDependencies: false, name: '', version: '', }; for (const depName in manifestFile.dependencies) { if (!manifestFile.dependencies.hasOwnProperty(depName)) { continue; } const depValue = manifestFile.dependencies[depName]; const version = depValue.version || depValue; const isDev = depValue.type === 'build'; depTree.hasDevDependencies = depTree.hasDevDependencies || isDev; if (isDev && !includeDev) { continue; } depTree.dependencies[depName] = buildSubTreeFromProjectJson(depName, version, isDev); } return depTree; } exports.getDependencyTreeFromProjectJson = getDependencyTreeFromProjectJson; function buildSubTreeFromProjectJson(name, version, isDev) { const depSubTree = { depType: isDev ? DepType.dev : DepType.prod, dependencies: {}, name, version, }; return depSubTree; } async function getDependencyTreeFromPackagesConfig(manifestFile, includeDev = false) { var _a, _b; const depTree = { dependencies: {}, hasDevDependencies: false, name: '', version: '', }; const packageList = (_b = (_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.packages) === null || _a === void 0 ? void 0 : _a.package) !== null && _b !== void 0 ? _b : []; for (const dep of packageList) { const depName = dep.$.id; const isDev = !!dep.$.developmentDependency; depTree.hasDevDependencies = depTree.hasDevDependencies || isDev; if (isDev && !includeDev) { continue; } depTree.dependencies[depName] = buildSubTreeFromPackagesConfig(dep, isDev); } return depTree; } exports.getDependencyTreeFromPackagesConfig = getDependencyTreeFromPackagesConfig; function buildSubTreeFromPackagesConfig(dep, isDev) { const depSubTree = { depType: isDev ? DepType.dev : DepType.prod, dependencies: {}, name: dep.$.id, version: dep.$.version, }; if (dep.$.targetFramework) { depSubTree.targetFrameworks = [dep.$.targetFramework]; } return depSubTree; } async function getDependencyTreeFromProjectFile(manifestFile, includeDev = false, propsMap = {}) { var _a, _b, _c, _d; const nameProperty = ((_b = (_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.Project) === null || _a === void 0 ? void 0 : _a.PropertyGroup) !== null && _b !== void 0 ? _b : []) .filter((propertyGroup) => typeof propertyGroup !== 'string') .find((propertyGroup) => { return 'PackageId' in propertyGroup || 'AssemblyName' in propertyGroup; }) || {}; const name = ((_c = nameProperty.PackageId) === null || _c === void 0 ? void 0 : _c[0]) || ((_d = nameProperty.AssemblyName) === null || _d === void 0 ? void 0 : _d[0]) || ''; const packageReferenceDeps = await getDependenciesFromPackageReference(manifestFile, includeDev, propsMap); // order matters, the order deps are parsed in needs to be preserved and first seen kept // so applying the packageReferenceDeps last to override the second parsed const depTree = { dependencies: Object.assign({}, packageReferenceDeps.dependencies), hasDevDependencies: packageReferenceDeps.hasDevDependencies, name, version: '', }; if (packageReferenceDeps.dependenciesWithUnknownVersions) { depTree.dependenciesWithUnknownVersions = packageReferenceDeps.dependenciesWithUnknownVersions; } return depTree; } exports.getDependencyTreeFromProjectFile = getDependencyTreeFromProjectFile; async function getDependenciesFromPackageReference(manifestFile, includeDev = false, propsMap) { var _a, _b; let dependenciesResult = { dependencies: {}, hasDevDependencies: false, }; const packageGroups = ((_b = (_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.Project) === null || _a === void 0 ? void 0 : _a.ItemGroup) !== null && _b !== void 0 ? _b : []).filter((itemGroup) => typeof itemGroup === 'object' && 'PackageReference' in itemGroup); if (!packageGroups.length) { return dependenciesResult; } for (const packageList of packageGroups) { dependenciesResult = processItemGroupForPackageReference(packageList, manifestFile, includeDev, dependenciesResult, propsMap); } return dependenciesResult; } exports.getDependenciesFromPackageReference = getDependenciesFromPackageReference; function processItemGroupForPackageReference(packageList, manifestFile, includeDev, dependenciesResult, propsMap) { var _a, _b; const targetFrameworks = ((_b = (_a = packageList === null || packageList === void 0 ? void 0 : packageList.$) === null || _a === void 0 ? void 0 : _a.Condition) !== null && _b !== void 0 ? _b : false) ? getConditionalFrameworks(packageList.$.Condition) : []; for (const dep of packageList.PackageReference) { const depName = dep.$.Include; if (!depName) { // PackageReference Update is not yet supported continue; } const isDev = !!dep.$.developmentDependency; dependenciesResult.hasDevDependencies = dependenciesResult.hasDevDependencies || isDev; if (isDev && !includeDev) { continue; } const subDep = buildSubTreeFromPackageReference(dep, isDev, manifestFile, targetFrameworks, propsMap); if (subDep.withoutVersion) { dependenciesResult.dependenciesWithUnknownVersions = dependenciesResult.dependenciesWithUnknownVersions || []; dependenciesResult.dependenciesWithUnknownVersions.push(subDep.name); } else { dependenciesResult.dependencies[depName] = subDep; } } return dependenciesResult; } function buildSubTreeFromPackageReference(dep, isDev, manifestFile, targetFrameworks, propsMap) { const version = extractDependencyVersion(dep, manifestFile, propsMap) || ''; if (!(0, lodash_1.isEmpty)(version)) { const depSubTree = { depType: isDev ? DepType.dev : DepType.prod, dependencies: {}, name: dep.$.Include, // Version could be in attributes or as child node. version, }; if (targetFrameworks.length) { depSubTree.targetFrameworks = targetFrameworks; } return depSubTree; } else { return { name: dep.$.Include, withoutVersion: true }; } } function extractDependencyVersion(dep, manifestFile, propsMap) { var _a, _b; const VARS_MATCHER = /^\$\((.*?)\)/; let version = ((_a = dep === null || dep === void 0 ? void 0 : dep.$) === null || _a === void 0 ? void 0 : _a.Version) || (dep === null || dep === void 0 ? void 0 : dep.Version); if (Array.isArray(version)) { version = version[0]; } const variableVersion = version && version.match(VARS_MATCHER); if (!variableVersion) { return version; } // version is a variable, extract it from manifest or props lookup const propertyName = variableVersion[1]; const propertyMap = Object.assign(Object.assign({}, propsMap), getPropertiesMap(manifestFile)); return (_b = propertyMap === null || propertyMap === void 0 ? void 0 : propertyMap[propertyName]) !== null && _b !== void 0 ? _b : null; } function getConditionalFrameworks(condition) { const regexp = /\(TargetFramework\)'\s?==\s? '((\w|\d|\.)*)'/g; const frameworks = []; let match = regexp.exec(condition); while (match !== null) { frameworks.push(match[1]); match = regexp.exec(condition); } return frameworks; } async function parseXmlFile(manifestFileContents) { return new Promise((resolve, reject) => { parseXML.parseString(manifestFileContents, (err, result) => { if (err) { const e = new error_catalog_nodejs_public_1.OpenSourceEcosystems.UnparseableManifestError('Manifest xml file parsing failed'); return reject(e); } return resolve(result); }); }); } exports.parseXmlFile = parseXmlFile; function getPropertiesMap(propsContents) { var _a, _b; const projectPropertyGroup = (_b = (_a = propsContents === null || propsContents === void 0 ? void 0 : propsContents.Project) === null || _a === void 0 ? void 0 : _a.PropertyGroup) !== null && _b !== void 0 ? _b : []; const props = {}; if (!projectPropertyGroup.length) { return props; } for (const group of projectPropertyGroup) { for (const key of Object.keys(group)) { (0, lodash_1.set)(props, key, group[key][0]); } } return props; } exports.getPropertiesMap = getPropertiesMap; function getTargetFrameworksFromProjectFile(manifestFile) { var _a, _b; let targetFrameworksResult = []; const projectPropertyGroup = (_b = (_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.Project) === null || _a === void 0 ? void 0 : _a.PropertyGroup) !== null && _b !== void 0 ? _b : []; if (!projectPropertyGroup) { return targetFrameworksResult; } let propertyList = {}; try { propertyList = projectPropertyGroup.find((propertyGroup) => { return ('TargetFramework' in propertyGroup || 'TargetFrameworks' in propertyGroup || 'TargetFrameworkVersion' in propertyGroup); }) || {}; } catch (err) { propertyList = {}; } if ((0, lodash_1.isEmpty)(propertyList)) { return targetFrameworksResult; } // TargetFrameworks is expected to be a list ; separated if (propertyList.TargetFrameworks) { for (const item of propertyList.TargetFrameworks) { targetFrameworksResult = [ ...targetFrameworksResult, ...getTargetFrameworks(item), ]; } } // TargetFrameworkVersion is expected to be a string containing only one item // TargetFrameworkVersion also implies .NETFramework, for convenience // return longer version if (propertyList.TargetFrameworkVersion) { targetFrameworksResult.push(`.NETFramework,Version=${propertyList.TargetFrameworkVersion[0]}`); } // TargetFrameworks is expected to be a string if (propertyList.TargetFramework) { // sanity check if (Array.isArray(propertyList.TargetFramework)) { // mutate the array to effectively "ignore" conditions propertyList.TargetFramework = propertyList.TargetFramework.map((framework) => { if (framework && typeof framework === 'object' && Object.hasOwnProperty.call(framework, '_')) { return framework._; } return framework; }); } targetFrameworksResult = [ ...targetFrameworksResult, ...propertyList.TargetFramework, ]; } return (0, lodash_1.uniq)(targetFrameworksResult); } exports.getTargetFrameworksFromProjectFile = getTargetFrameworksFromProjectFile; // Extracts the SDK name for SDK-style projects, based on documentation at // https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview. function getSdkFromProjectFile(manifestFile) { var _a, _b, _c, _d, _e, _f; const projectSdkAttribute = (_b = (_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.Project) === null || _a === void 0 ? void 0 : _a.$) === null || _b === void 0 ? void 0 : _b.Sdk; const topLevelSdkElement = (_f = (_e = (_d = (_c = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.Project) === null || _c === void 0 ? void 0 : _c.Sdk) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.$) === null || _f === void 0 ? void 0 : _f.Name; return projectSdkAttribute || topLevelSdkElement; } exports.getSdkFromProjectFile = getSdkFromProjectFile; function getTargetFrameworks(item) { if (typeof item === 'object' && Object.hasOwnProperty.call(item, '_')) { item = item._; } return item.split(';').filter((x) => !(0, lodash_1.isEmpty)(x)); } function getTargetFrameworksFromProjectConfig(manifestFile) { var _a, _b; const targetFrameworksResult = []; const packages = (_b = (_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.packages) === null || _a === void 0 ? void 0 : _a.package) !== null && _b !== void 0 ? _b : []; for (const item of packages) { const targetFramework = item.$.targetFramework; if (!targetFramework) { continue; } if (!targetFrameworksResult.includes(targetFramework)) { targetFrameworksResult.push(targetFramework); } } return targetFrameworksResult; } exports.getTargetFrameworksFromProjectConfig = getTargetFrameworksFromProjectConfig; function getTargetFrameworksFromProjectJson(manifestFile) { var _a; return Object.keys((_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.frameworks) !== null && _a !== void 0 ? _a : {}); } exports.getTargetFrameworksFromProjectJson = getTargetFrameworksFromProjectJson; function getTargetFrameworksFromProjectAssetsJson(manifestFile) { var _a; return Object.keys((_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.targets) !== null && _a !== void 0 ? _a : {}); } exports.getTargetFrameworksFromProjectAssetsJson = getTargetFrameworksFromProjectAssetsJson; //# sourceMappingURL=index.js.map