dotnet-deps-parser
Version:
Generate a dep tree given a collection of manifests
236 lines • 13.6 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.DepType = exports.isSupportedByV3GraphGeneration = exports.isSupportedByV2GraphGeneration = exports.extractProps = exports.extractTargetSdkFromGlobalJson = exports.extractTargetFrameworksFromProjectAssetsJson = exports.extractTargetFrameworksFromProjectJson = exports.extractTargetFrameworksFromProjectConfig = exports.extractTargetFrameworksFromProjectFile = exports.extractTargetFrameworksFromFiles = exports.extractProjectSdkFromProjectFile = exports.containsPackageReference = exports.buildDepTreeFromFiles = exports.buildDepTreeFromProjectAssetsJson = exports.buildDepTreeFromProjectJson = exports.buildDepTreeFromProjectFile = exports.buildDepTreeFromPackagesConfig = void 0;
require("source-map-support/register");
const fs = require("fs");
const path = require("path");
const error_catalog_nodejs_public_1 = require("@snyk/error-catalog-nodejs-public");
const jsonc = require("jsonc-parser");
const parsers_1 = require("./parsers");
Object.defineProperty(exports, "DepType", { enumerable: true, get: function () { return parsers_1.DepType; } });
const project_assets_json_parser_1 = require("./parsers/project-assets-json-parser");
const PROJ_FILE_EXTENSIONS = ['.csproj', '.vbproj', '.fsproj'];
function buildDepTreeFromProjectJson(manifestFileContents, includeDev = false) {
// trimming required to address files with UTF-8 with BOM encoding
const manifestFile = JSON.parse(manifestFileContents.trim());
return (0, parsers_1.getDependencyTreeFromProjectJson)(manifestFile, includeDev);
}
exports.buildDepTreeFromProjectJson = buildDepTreeFromProjectJson;
// TODO: Figure out what to do about devDeps
function buildDepTreeFromProjectAssetsJson(manifestFileContents, targetFramework) {
if (!targetFramework) {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.MissingPayloadError('Missing targetFramework for project.assets.json');
}
// trimming required to address files with UTF-8 with BOM encoding
const manifestFile = JSON.parse(manifestFileContents.trim());
return (0, project_assets_json_parser_1.getDependencyTreeFromProjectAssetsJson)(manifestFile, targetFramework);
}
exports.buildDepTreeFromProjectAssetsJson = buildDepTreeFromProjectAssetsJson;
async function buildDepTreeFromPackagesConfig(manifestFileContents, includeDev = false) {
const manifestFile = await (0, parsers_1.parseXmlFile)(manifestFileContents);
return (0, parsers_1.getDependencyTreeFromPackagesConfig)(manifestFile, includeDev);
}
exports.buildDepTreeFromPackagesConfig = buildDepTreeFromPackagesConfig;
async function buildDepTreeFromProjectFile(manifestFileContents, includeDev = false, propsMap = {}) {
const manifestFile = await (0, parsers_1.parseXmlFile)(manifestFileContents);
return (0, parsers_1.getDependencyTreeFromProjectFile)(manifestFile, includeDev, propsMap);
}
exports.buildDepTreeFromProjectFile = buildDepTreeFromProjectFile;
function buildDepTreeFromFiles(root, manifestFilePath, includeDev = false, targetFramework) {
if (!root || !manifestFilePath) {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.MissingPayloadError('Missing required parameters for building dependency tree from files');
}
const manifestFileFullPath = path.resolve(root, manifestFilePath);
if (!fs.existsSync(manifestFileFullPath)) {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.CannotGetFileFromSourceError('No packages.config, project.json or project file found', {
location: manifestFileFullPath,
});
}
const manifestFileContents = fs.readFileSync(manifestFileFullPath, 'utf-8');
const manifestFileExtension = path.extname(manifestFileFullPath);
if (PROJ_FILE_EXTENSIONS.includes(manifestFileExtension)) {
return buildDepTreeFromProjectFile(manifestFileContents, includeDev);
}
else if (manifestFilePath.endsWith('packages.config')) {
return buildDepTreeFromPackagesConfig(manifestFileContents, includeDev);
}
else if (manifestFilePath.endsWith('project.json')) {
return buildDepTreeFromProjectJson(manifestFileContents, includeDev);
}
else if (manifestFilePath.endsWith('project.assets.json')) {
return buildDepTreeFromProjectAssetsJson(manifestFileContents, targetFramework);
}
else {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.UnsupportedManifestFileError('Unsupported file, please provide ' +
'either packages.config or project file.', {
location: manifestFilePath,
});
}
}
exports.buildDepTreeFromFiles = buildDepTreeFromFiles;
// The V2 project aimed at removing virtually all of this reinvention of the wheel logic in favor of utilization of
// the `dotnet` cli itself, publicly referred to as just 'V2', is done iteratively. Since this package is shared with
// both internal and external packages that all make up parts of the V2 project, we add the shared logic here.
// Further, at least to keep project development iterative, don't support needle and haystack'ing dependency JSON
// for target frameworks other than .NET 5+ and .NET Core, as other frameworks generates vastly other types of
// .json graphs, requiring a whole other parsing strategy to extract tne runtime dependencies.
// For a list of version naming currently available, see
// https://learn.microsoft.com/en-us/dotnet/standard/frameworks#supported-target-frameworks
function isSupportedByV2GraphGeneration(targetFramework) {
var _a;
// Everything that does not start with 'net' is already game over. E.g. Windows Phone (wp) or silverlight (sl) etc.
if (!targetFramework.startsWith('net')) {
return false;
}
// - .NET Core: netcoreappN.N, - EOL from Microsoft
if (targetFramework.startsWith('netcoreapp')) {
return false;
}
// What's left is:
// - .NET 5+ netN.N, (supported)
// - .NET Standard: netstandardN.N (supported) and
// - .NET Framework: netNNN (unsupported)
// So if there's a dot, we're good.
if (targetFramework.includes('.')) {
// Ensure that if it's "netN.N", we don't accept anything below 4.0.
// It's not valid to supply something below 5 with dots (i.e. net4.8, should be net48 per the documentation
// links above), but it's an easy mistake to make, and it's still accepted by the dotnet CLI.
const regex = /net(?<major>\d)\.(?<minor>\d)/gm;
const match = regex.exec(targetFramework);
if (match) {
const major = parseInt(((_a = match.groups) === null || _a === void 0 ? void 0 : _a.major) || '0', 10);
return major >= 5;
}
return true;
}
// Otherwise it's something before .NET 5 and we're out
return false;
}
exports.isSupportedByV2GraphGeneration = isSupportedByV2GraphGeneration;
// The V3 uses PackageOverrides files from the dotnet SDK to resolve the version
// of packages shipped with the dotnet SDK rather than downloaded from Nuget.
// The logic works for any project using a supported project SDK, see
// https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview.
function isSupportedByV3GraphGeneration(targetFramework, projectSdk) {
// TargetFramework is required for valid projects.
if (!targetFramework) {
return false;
}
// What's been tested:
// - EOL targets: Windows Phone (wp), Silverlight (sl), .NET Core: netcoreappN.N
// - .NET 5+ netN.N
// - .NET Standard: netstandardN.N
// - .NET Framework: netNN or netNNN
// As long as they use a supported SDK style, they can be scanned.
// These are the SDKs that produce the necessary obj/project.assets.json file
// with the project name and target framework dependencies.
// Uno imports the Microsoft.NET.Sdk behind the scene, so is also supported.
return [
'Microsoft.NET.Sdk',
'MSBuild.Sdk.Extras',
'MSTest.Sdk',
'Uno.Sdk',
].some((sdk) => (projectSdk || '').startsWith(sdk));
}
exports.isSupportedByV3GraphGeneration = isSupportedByV3GraphGeneration;
function extractTargetFrameworksFromFiles(root, manifestFilePath) {
if (!root || !manifestFilePath) {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.MissingPayloadError('Missing required parameters for extractTargetFrameworksFromFiles()');
}
const manifestFileFullPath = path.resolve(root, manifestFilePath);
if (!fs.existsSync(manifestFileFullPath)) {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.CannotGetFileFromSourceError('No project file found', {
location: manifestFileFullPath,
});
}
const manifestFileContents = fs.readFileSync(manifestFileFullPath, 'utf-8');
const manifestFileExtension = path.extname(manifestFileFullPath);
if (PROJ_FILE_EXTENSIONS.includes(manifestFileExtension)) {
return extractTargetFrameworksFromProjectFile(manifestFileContents);
}
else if (manifestFilePath.endsWith('packages.config')) {
return extractTargetFrameworksFromProjectConfig(manifestFileContents);
}
else if (manifestFilePath.endsWith('project.json')) {
return extractTargetFrameworksFromProjectJson(manifestFileContents);
}
else if (manifestFilePath.endsWith('project.assets.json')) {
return extractTargetFrameworksFromProjectAssetsJson(manifestFileContents);
}
else {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.UnsupportedManifestFileError('Unsupported file, please provide ' +
'a project *.csproj, *.vbproj, *.fsproj or packages.config file.', {
location: manifestFilePath,
});
}
}
exports.extractTargetFrameworksFromFiles = extractTargetFrameworksFromFiles;
async function extractProjectSdkFromProjectFile(manifestFileContents) {
const manifestFile = await (0, parsers_1.parseXmlFile)(manifestFileContents);
return (0, parsers_1.getSdkFromProjectFile)(manifestFile);
}
exports.extractProjectSdkFromProjectFile = extractProjectSdkFromProjectFile;
async function extractTargetFrameworksFromProjectFile(manifestFileContents) {
const manifestFile = await (0, parsers_1.parseXmlFile)(manifestFileContents);
return (0, parsers_1.getTargetFrameworksFromProjectFile)(manifestFile);
}
exports.extractTargetFrameworksFromProjectFile = extractTargetFrameworksFromProjectFile;
async function extractTargetFrameworksFromProjectConfig(manifestFileContents) {
const manifestFile = await (0, parsers_1.parseXmlFile)(manifestFileContents);
return (0, parsers_1.getTargetFrameworksFromProjectConfig)(manifestFile);
}
exports.extractTargetFrameworksFromProjectConfig = extractTargetFrameworksFromProjectConfig;
async function containsPackageReference(manifestFileContents) {
var _a, _b;
const manifestFile = await (0, parsers_1.parseXmlFile)(manifestFileContents);
const projectItems = (_b = (_a = manifestFile === null || manifestFile === void 0 ? void 0 : manifestFile.Project) === null || _a === void 0 ? void 0 : _a.ItemGroup) !== null && _b !== void 0 ? _b : [];
const referenceIndex = projectItems.findIndex((itemGroup) => typeof itemGroup === 'object' && 'PackageReference' in itemGroup);
return referenceIndex !== -1;
}
exports.containsPackageReference = containsPackageReference;
async function extractTargetFrameworksFromProjectJson(manifestFileContents) {
let manifestFile;
try {
// trimming required to address files with UTF-8 with BOM encoding
manifestFile = JSON.parse(manifestFileContents.trim());
}
catch (err) {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.UnparseableManifestError('Failed to parse manifest file');
}
return (0, parsers_1.getTargetFrameworksFromProjectJson)(manifestFile);
}
exports.extractTargetFrameworksFromProjectJson = extractTargetFrameworksFromProjectJson;
async function extractTargetFrameworksFromProjectAssetsJson(manifestFileContents) {
let manifestFile;
try {
// trimming required to address files with UTF-8 with BOM encoding
manifestFile = JSON.parse(manifestFileContents.trim());
}
catch (err) {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.UnparseableManifestError('Failed to parse manifest file');
}
return (0, parsers_1.getTargetFrameworksFromProjectAssetsJson)(manifestFile);
}
exports.extractTargetFrameworksFromProjectAssetsJson = extractTargetFrameworksFromProjectAssetsJson;
function extractTargetSdkFromGlobalJson(manifestFileContents) {
var _a;
try {
// Use a JSONC parser as that's the format of global.json, which accepts comments,
// see https://learn.microsoft.com/en-us/dotnet/core/tools/global-json#comments-in-globaljson
const globalJsonAsObj = jsonc.parse(manifestFileContents);
return (_a = globalJsonAsObj === null || globalJsonAsObj === void 0 ? void 0 : globalJsonAsObj.sdk) === null || _a === void 0 ? void 0 : _a.version;
}
catch (err) {
throw new Error(`Extracting target framework failed with error ${err.message}`);
}
}
exports.extractTargetSdkFromGlobalJson = extractTargetSdkFromGlobalJson;
async function extractProps(propsFileContents) {
const propsFile = await (0, parsers_1.parseXmlFile)(propsFileContents);
if (!propsFile) {
throw new error_catalog_nodejs_public_1.OpenSourceEcosystems.MissingPayloadError('Empty xml file');
}
return (0, parsers_1.getPropertiesMap)(propsFile);
}
exports.extractProps = extractProps;
//# sourceMappingURL=index.js.map
;