dotnet-deps-parser
Version:
Generate a dep tree given a collection of manifests
325 lines • 15.1 kB
JavaScript
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
;