UNPKG

dotnet-deps-parser

Version:

Generate a dep tree given a collection of manifests

236 lines 13.6 kB
"use strict"; 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