Skip to content

Commit

Permalink
Track pnpm workspace for each component (#1578)
Browse files Browse the repository at this point in the history
* Track the pnpm workspace that seeks each component

Signed-off-by: Prabhu Subramanian <[email protected]>

* Track git packages

Signed-off-by: Prabhu Subramanian <[email protected]>

---------

Signed-off-by: Prabhu Subramanian <[email protected]>
  • Loading branch information
prabhu authored Jan 19, 2025
1 parent 8d7080b commit a11433f
Show file tree
Hide file tree
Showing 10 changed files with 548 additions and 33 deletions.
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyclonedx/cdxgen",
"version": "11.1.0",
"version": "11.1.1",
"exports": "./lib/cli/index.js",
"compilerOptions": {
"lib": ["deno.window"],
Expand Down
2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyclonedx/cdxgen",
"version": "11.1.0",
"version": "11.1.1",
"exports": "./lib/cli/index.js",
"include": ["*.js", "lib/**", "bin/**", "data/**", "types/**"],
"exclude": [
Expand Down
77 changes: 76 additions & 1 deletion lib/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ import {
parsePkgJson,
parsePkgLock,
parsePnpmLock,
parsePnpmWorkspace,
parsePom,
parsePrivadoFile,
parsePubLockData,
Expand Down Expand Up @@ -2310,6 +2311,11 @@ export async function createNodejsBom(path, options) {
`${options.multiProject ? "**/" : ""}pnpm-lock.yaml`,
options,
);
const pnpmWorkspaceFiles = getAllFiles(
path,
`${options.multiProject ? "**/" : ""}pnpm-workspace.yaml`,
options,
);
const minJsFiles = getAllFiles(
path,
`${options.multiProject ? "**/" : ""}*min.js`,
Expand Down Expand Up @@ -2479,6 +2485,67 @@ export async function createNodejsBom(path, options) {
isPackageManagerAllowed("pnpm", ["npm", "yarn", "rush"], options)
) {
manifestFiles = manifestFiles.concat(pnpmLockFiles);
const workspacePackages = [];
const workspaceSrcFiles = {};
const workspaceDirectDeps = {};
const depsWorkspaceRefs = {};
let workspaceCatalogs = {};
let workspaceWarningShown = false;
// Is this a pnpm workspace?
for (const f of pnpmWorkspaceFiles) {
if (DEBUG_MODE) {
console.log(`Parsing workspace definition ${f}`);
}
const workspaceObj = parsePnpmWorkspace(f);
if (workspaceObj?.packages) {
// We need the precise purl for all workspace packages and their direct dependencies
for (const awp of workspaceObj.packages) {
const wpkgJsonFiles = getAllFiles(awp, "**/package.json", options);
if (!wpkgJsonFiles?.length) {
if (!workspaceWarningShown) {
workspaceWarningShown = true;
console.log(
`Unable to find any package.json files belonging to the workspace '${awp}' referred in ${f}. To improve SBOM precision, run cdxgen from the directory containing the complete source code.`,
);
}
continue;
}
for (const apj of wpkgJsonFiles) {
const pkgData = JSON.parse(readFileSync(apj, "utf-8"));
if (pkgData?.name) {
let workspaceRef = `pkg:npm/${pkgData.name}`;
if (pkgData?.version) {
workspaceRef = `${workspaceRef}@${pkgData.version}`;
}
// Track all workspace purls
workspacePackages.push(workspaceRef);
workspaceSrcFiles[workspaceRef] = apj;
// Track the direct dependencies of each workspace and workspace refs for each direct deps.
const allDeps = {
...(pkgData.dependencies || {}),
...(pkgData.devDependencies || {}),
...(pkgData.peerDependencies || {}),
};
for (const adep of Object.keys(allDeps)) {
if (!workspaceDirectDeps[workspaceRef]) {
workspaceDirectDeps[workspaceRef] = new Set();
}
const apkgRef = `pkg:npm/${adep}`;
workspaceDirectDeps[workspaceRef].add(apkgRef);
if (!depsWorkspaceRefs[apkgRef]) {
depsWorkspaceRefs[apkgRef] = new Set();
}
depsWorkspaceRefs[apkgRef].add(workspaceRef);
}
}
}
}
}
workspaceCatalogs = {
...workspaceCatalogs,
...(workspaceObj.catalogs || {}),
};
}
for (const f of pnpmLockFiles) {
if (DEBUG_MODE) {
console.log(`Parsing ${f}`);
Expand Down Expand Up @@ -2527,7 +2594,15 @@ export async function createNodejsBom(path, options) {
}
}
// Parse the pnpm file
const parsedList = await parsePnpmLock(f, parentComponent);
const parsedList = await parsePnpmLock(
f,
parentComponent,
workspacePackages,
workspaceSrcFiles,
workspaceCatalogs,
workspaceDirectDeps,
depsWorkspaceRefs,
);
const dlist = parsedList.pkgList;
if (dlist?.length) {
pkgList = pkgList.concat(dlist);
Expand Down
Loading

0 comments on commit a11433f

Please sign in to comment.