snyk-go-parser
Version:
Generate a dep tree given a collection of manifests
98 lines • 3.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseGoModGraph = parseGoModGraph;
const semver = require("@snyk/go-semver");
const dep_graph_1 = require("@snyk/dep-graph");
const types_1 = require("../types");
// Modules can be of shape `modules/snyk/inner/v2` or `modules/snyk/v2/inner`
const GO_SEMVER_PREFIXED_MODULES_REGEX = /(.*)\/v[0-9]+(.*)/;
const GO_MODULES = 'gomodules';
function parseGoModGraph(goModGraphOutput, projectName, projectVersion = types_1.DEFAULT_INITIAL_VERSION, opts = { pruneRepeatedSubDependencies: true }) {
var _a;
const lines = extractLines(goModGraphOutput);
const maxVersions = getMaxVersions(lines);
const depMap = getChildrenByParent(lines);
const rootPkgInfo = {
name: projectName || ((_a = lines[0]) === null || _a === void 0 ? void 0 : _a.parent.name) || 'empty',
version: projectVersion,
};
const dgBuilder = new dep_graph_1.DepGraphBuilder({ name: GO_MODULES }, rootPkgInfo);
dfsVisit(dgBuilder, depMap, maxVersions, opts.pruneRepeatedSubDependencies, rootPkgInfo);
return dgBuilder.build();
}
function dfsVisit(dgBuilder, depMap, maxVersions, prune, pkg, visited) {
const isRoot = visited === undefined;
const nodeId = `${pkg.name}@${pkg.version}`;
const parentId = isRoot ? 'root-node' : nodeId;
if (isRoot && !prune) {
visited = new Set();
}
const deps = depMap[nodeId] || [];
for (const { name, version } of deps) {
const localVisited = visited || new Set();
const effectiveDep = { name, version: maxVersions.get(name) || version };
const depId = `${effectiveDep.name}@${effectiveDep.version}`;
if (localVisited.has(depId)) {
if (prune) {
const prunedId = `${depId}:pruned`;
dgBuilder.addPkgNode(effectiveDep, prunedId, {
labels: { pruned: 'true' },
});
dgBuilder.connectDep(parentId, prunedId);
}
else {
dgBuilder.connectDep(parentId, depId);
}
continue;
}
dgBuilder.addPkgNode(effectiveDep, depId);
dgBuilder.connectDep(parentId, depId);
localVisited.add(depId);
dfsVisit(dgBuilder, depMap, maxVersions, prune, effectiveDep, localVisited);
}
}
function extractLines(goModGraphOutput) {
return goModGraphOutput
.trim()
.split('\n')
.filter(Boolean)
.map((line) => {
const [parent, child] = line
.trim()
.split(/\s/)
.map((item) => item.split('@'))
.map(([name, version]) => ({
// Handle cases for prefixed-semver
// see https://golang.org/ref/mod#major-version-suffixes
name: name.replace(GO_SEMVER_PREFIXED_MODULES_REGEX, '$1$2'),
version: version,
}));
return { parent, child };
});
}
function getMaxVersions(lines) {
const maxVersions = new Map();
for (const line of lines) {
const { child } = line;
if (!maxVersions.has(child.name) ||
semver.gt(child.version, maxVersions.get(child.name))) {
maxVersions.set(child.name, child.version);
}
}
return maxVersions;
}
function getChildrenByParent(lines) {
const childrenByParent = {};
for (const line of lines) {
const { parent, child } = line;
const parentId = `${parent.name}@${parent.version || types_1.DEFAULT_INITIAL_VERSION}`;
if (parentId in childrenByParent) {
childrenByParent[parentId].push(child);
}
else {
childrenByParent[parentId] = [child];
}
}
return childrenByParent;
}
//# sourceMappingURL=gomod-graph-parser.js.map