From b1d9bb8c69dcdd2cd4c01f2d04c0ae0f34a0ef7d Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sat, 14 Sep 2024 23:39:18 +0530 Subject: [PATCH 01/11] add logic to find additional components and deps Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 214 ++++++++++++++++++++++++++++--------------- 1 file changed, 142 insertions(+), 72 deletions(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index 1907d483a..278c220e4 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1726,6 +1726,18 @@ function _markTreeOptional( } } +function getVersionNumPnpm(depPkg){ + let version; + if (typeof version === "object" && depPkg.version) { + version = depPkg.version; + } + // version: 3.0.1(ajv@8.14.0) + if (version?.includes("(")) { + version = version.split("(")[0]; + } + return version; +} + /** * Parse nodejs pnpm lock file * @@ -1770,86 +1782,144 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { } // This logic matches the pnpm list command to include only direct dependencies if (ppurl !== "") { + try { + // In lock file version 9, direct dependencies is under importers - const rootDirectDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.dependencies || {} - : yamlObj.dependencies || {}; - const rootDevDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.devDependencies || {} - : {}; - const rootOptionalDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.optionalDependencies || {} - : {}; - const ddeplist = []; - // Find the root optional dependencies - for (const rdk of Object.keys(rootDevDeps)) { - let version = rootDevDeps[rdk]; - if (typeof version === "object" && version.version) { - version = version.version; - } - // version: 3.0.1(ajv@8.14.0) - if (version?.includes("(")) { - version = version.split("(")[0]; + const rootDirectDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.dependencies || {} + : yamlObj.dependencies || {}; + const rootDevDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.devDependencies || {} + : {}; + const rootOptionalDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.optionalDependencies || {} + : {}; + const ddeplist = []; + // Find the root optional dependencies + for (const rdk of Object.keys(rootDevDeps)) { + let version = getVersionNumPnpm(rootDevDeps[rdk]); + const dpurl = new PackageURL( + "npm", + "", + rdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } - const dpurl = new PackageURL( - "npm", - "", - rdk, - version, - null, - null, - ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; - } - for (const rdk of Object.keys(rootOptionalDeps)) { - let version = rootOptionalDeps[rdk]; - if (typeof version === "object" && version.version) { - version = version.version; + for (const rdk of Object.keys(rootOptionalDeps)) { + let version = getVersionNumPnpm(rootOptionalDeps[rdk]); + const dpurl = new PackageURL( + "npm", + "", + rdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } - // version: 3.0.1(ajv@8.14.0) - if (version?.includes("(")) { - version = version.split("(")[0]; + for (const dk of Object.keys(rootDirectDeps)) { + let version = getVersionNumPnpm(rootDirectDeps[dk]); + const dpurl = new PackageURL( + "npm", + "", + dk, + version, + null, + null, + ).toString(); + ddeplist.push(decodeURIComponent(dpurl)); + if (lockfileVersion >= 9) { + // These are direct dependencies so cannot be optional + possibleOptionalDeps[decodeURIComponent(dpurl)] = false; + } } - const dpurl = new PackageURL( - "npm", - "", - rdk, - version, - null, - null, - ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; - } - for (const dk of Object.keys(rootDirectDeps)) { - let version = rootDirectDeps[dk]; - if (typeof version === "object" && version.version) { - version = version.version; + + dependenciesList.push({ + ref: decodeURIComponent(ppurl), + dependsOn: ddeplist, + }); + + // pnpm-lock.yaml contains more than root dependencies in importers + // TODO: verify if this is a good way to check. + // we do what we did above but for all the other components + for (const importedComponentName of Object.keys(yamlObj['importers'])) { + + const componentDeps = yamlObj['importers'][importedComponentName]['dependencies'] || {}; + const componentDevDeps = yamlObj['importers'][importedComponentName]['devDependencies'] || {}; + const optionalDeps = yamlObj['importers'][importedComponentName]['optionalDependencies'] || {}; + + // TODO: verify as we had seen before with rush there can be ../name in pnpm-lock-yaml + let compPurl = new PackageURL( + "npm", + parentComponent.group, + parentComponent.name + "/" + importedComponentName, + parentComponent.version, + null, + null, + ).toString(); + // Find the component optional dependencies + let comDepList = []; + for (const cdk of Object.keys(componentDeps)) { + let version = getVersionNumPnpm(componentDeps[cdk]); + const dpurl = new PackageURL( + "npm", + "", + cdk, + version, + null, + null, + ).toString(); + comDepList.push(decodeURIComponent(dpurl)) + // TODO: verify if this is possibleOptionalDeps false + possibleOptionalDeps[decodeURIComponent(dpurl)] = false; } - // version: 3.0.1(ajv@8.14.0) - if (version?.includes("(")) { - version = version.split("(")[0]; + + dependenciesList.push({ + ref: decodeURIComponent(compPurl), + dependsOn: comDepList, + }); + + for (const cdk of Object.keys(componentDevDeps)) { + let version = getVersionNumPnpm(componentDevDeps[cdk]); + const dpurl = new PackageURL( + "npm", + "", + cdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } - const dpurl = new PackageURL( - "npm", - "", - dk, - version, - null, - null, - ).toString(); - ddeplist.push(decodeURIComponent(dpurl)); - if (lockfileVersion >= 9) { - // These are direct dependencies so cannot be optional - possibleOptionalDeps[decodeURIComponent(dpurl)] = false; + + for (const cdk of Object.keys(optionalDeps)) { + let version = getVersionNumPnpm(optionalDeps[cdk]); + const dpurl = new PackageURL( + "npm", + "", + cdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } } - dependenciesList.push({ - ref: decodeURIComponent(ppurl), - dependsOn: ddeplist, - }); + + + } + catch(e){ + // Error may be due to yamlObj not being defined + if(DEBUG_MODE){ + console.log("Error while parsing pnpm lock file" + e); + } + } } const packages = yamlObj.packages || {}; // snapshots is a new key under lockfile version 9 From adc99fca12a432d8c5b226708f6b305c14193a34 Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sat, 14 Sep 2024 23:57:38 +0530 Subject: [PATCH 02/11] utilize just the last part of component name Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index 278c220e4..f24b9e778 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1853,12 +1853,20 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { const componentDeps = yamlObj['importers'][importedComponentName]['dependencies'] || {}; const componentDevDeps = yamlObj['importers'][importedComponentName]['devDependencies'] || {}; const optionalDeps = yamlObj['importers'][importedComponentName]['optionalDependencies'] || {}; + + let name = importedComponentName.split('/'); + name = name[name.length -1]; + + // if name is '.' continue loop + if (name == '.'){ + continue; + } // TODO: verify as we had seen before with rush there can be ../name in pnpm-lock-yaml let compPurl = new PackageURL( "npm", parentComponent.group, - parentComponent.name + "/" + importedComponentName, + parentComponent.name + "/" + name, parentComponent.version, null, null, From f8171bc0a24d9b4e0fe44d4825a804746dedc6ba Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sun, 15 Sep 2024 00:13:26 +0530 Subject: [PATCH 03/11] update according to purl spec Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 127 ++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index f24b9e778..e8f80e1a5 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1785,65 +1785,65 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { try { // In lock file version 9, direct dependencies is under importers - const rootDirectDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.dependencies || {} - : yamlObj.dependencies || {}; - const rootDevDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.devDependencies || {} - : {}; - const rootOptionalDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.optionalDependencies || {} - : {}; - const ddeplist = []; - // Find the root optional dependencies - for (const rdk of Object.keys(rootDevDeps)) { - let version = getVersionNumPnpm(rootDevDeps[rdk]); - const dpurl = new PackageURL( - "npm", - "", - rdk, - version, - null, - null, - ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; - } - for (const rdk of Object.keys(rootOptionalDeps)) { - let version = getVersionNumPnpm(rootOptionalDeps[rdk]); - const dpurl = new PackageURL( - "npm", - "", - rdk, - version, - null, - null, - ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; - } - for (const dk of Object.keys(rootDirectDeps)) { - let version = getVersionNumPnpm(rootDirectDeps[dk]); - const dpurl = new PackageURL( - "npm", - "", - dk, - version, - null, - null, - ).toString(); - ddeplist.push(decodeURIComponent(dpurl)); - if (lockfileVersion >= 9) { - // These are direct dependencies so cannot be optional - possibleOptionalDeps[decodeURIComponent(dpurl)] = false; - } + const rootDirectDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.dependencies || {} + : yamlObj.dependencies || {}; + const rootDevDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.devDependencies || {} + : {}; + const rootOptionalDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.optionalDependencies || {} + : {}; + const ddeplist = []; + // Find the root optional dependencies + for (const rdk of Object.keys(rootDevDeps)) { + let version = getVersionNumPnpm(rootDevDeps[rdk]); + const dpurl = new PackageURL( + "npm", + "", + rdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; + } + for (const rdk of Object.keys(rootOptionalDeps)) { + let version = getVersionNumPnpm(rootOptionalDeps[rdk]); + const dpurl = new PackageURL( + "npm", + "", + rdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; + } + for (const dk of Object.keys(rootDirectDeps)) { + let version = getVersionNumPnpm(rootDirectDeps[dk]); + const dpurl = new PackageURL( + "npm", + "", + dk, + version, + null, + null, + ).toString(); + ddeplist.push(decodeURIComponent(dpurl)); + if (lockfileVersion >= 9) { + // These are direct dependencies so cannot be optional + possibleOptionalDeps[decodeURIComponent(dpurl)] = false; } + } - dependenciesList.push({ - ref: decodeURIComponent(ppurl), - dependsOn: ddeplist, - }); + dependenciesList.push({ + ref: decodeURIComponent(ppurl), + dependsOn: ddeplist, + }); // pnpm-lock.yaml contains more than root dependencies in importers // TODO: verify if this is a good way to check. @@ -1853,23 +1853,26 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { const componentDeps = yamlObj['importers'][importedComponentName]['dependencies'] || {}; const componentDevDeps = yamlObj['importers'][importedComponentName]['devDependencies'] || {}; const optionalDeps = yamlObj['importers'][importedComponentName]['optionalDependencies'] || {}; - + let name = importedComponentName.split('/'); - name = name[name.length -1]; + let lastname = name[name.length -1]; + + let subpath = "name.filter(part => part !== '.' && part !== '..').join('/'); // if name is '.' continue loop - if (name == '.'){ + if (name == ""){ continue; } // TODO: verify as we had seen before with rush there can be ../name in pnpm-lock-yaml + // subpath not added here let compPurl = new PackageURL( "npm", parentComponent.group, - parentComponent.name + "/" + name, + parentComponent.name + "/" + lastname, parentComponent.version, null, - null, + subpath, ).toString(); // Find the component optional dependencies let comDepList = []; From c49999c7b078fd2b35b07e3f679909f8d37d6679 Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sun, 15 Sep 2024 00:14:43 +0530 Subject: [PATCH 04/11] fix Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index e8f80e1a5..842555cca 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1857,7 +1857,7 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { let name = importedComponentName.split('/'); let lastname = name[name.length -1]; - let subpath = "name.filter(part => part !== '.' && part !== '..').join('/'); + let subpath = name.filter(part => part !== '.' && part !== '..').join('/'); // if name is '.' continue loop if (name == ""){ @@ -1865,7 +1865,6 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { } // TODO: verify as we had seen before with rush there can be ../name in pnpm-lock-yaml - // subpath not added here let compPurl = new PackageURL( "npm", parentComponent.group, From 4830edffdd7fc03ee1b65bfb642d181603570144 Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sun, 15 Sep 2024 00:15:44 +0530 Subject: [PATCH 05/11] fix comment Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index 842555cca..eeeb444b2 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1859,7 +1859,7 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { let subpath = name.filter(part => part !== '.' && part !== '..').join('/'); - // if name is '.' continue loop + // if component name is '.' continue loop if (name == ""){ continue; } From 7148386e8221dbce0492e85a2c355419793977c1 Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sun, 15 Sep 2024 00:17:45 +0530 Subject: [PATCH 06/11] fix . Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index eeeb444b2..7b79a3327 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1860,7 +1860,7 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { let subpath = name.filter(part => part !== '.' && part !== '..').join('/'); // if component name is '.' continue loop - if (name == ""){ + if (lastname == "."){ continue; } From 5ae3ddf7c3763e0988cb666b2a4c39342f8a4364 Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Tue, 17 Sep 2024 19:47:49 +0530 Subject: [PATCH 07/11] add requested changes lint Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 239 +++++++++++++++++++++---------------------- 1 file changed, 117 insertions(+), 122 deletions(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index 7b79a3327..496133332 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1726,7 +1726,7 @@ function _markTreeOptional( } } -function getVersionNumPnpm(depPkg){ +function getVersionNumPnpm(depPkg) { let version; if (typeof version === "object" && depPkg.version) { version = depPkg.version; @@ -1782,155 +1782,150 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { } // This logic matches the pnpm list command to include only direct dependencies if (ppurl !== "") { - try { - - // In lock file version 9, direct dependencies is under importers - const rootDirectDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.dependencies || {} - : yamlObj.dependencies || {}; - const rootDevDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.devDependencies || {} - : {}; - const rootOptionalDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.optionalDependencies || {} - : {}; - const ddeplist = []; - // Find the root optional dependencies - for (const rdk of Object.keys(rootDevDeps)) { - let version = getVersionNumPnpm(rootDevDeps[rdk]); - const dpurl = new PackageURL( - "npm", - "", - rdk, - version, - null, - null, - ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; - } - for (const rdk of Object.keys(rootOptionalDeps)) { - let version = getVersionNumPnpm(rootOptionalDeps[rdk]); - const dpurl = new PackageURL( - "npm", - "", - rdk, - version, - null, - null, - ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; - } - for (const dk of Object.keys(rootDirectDeps)) { - let version = getVersionNumPnpm(rootDirectDeps[dk]); - const dpurl = new PackageURL( - "npm", - "", - dk, - version, - null, - null, - ).toString(); - ddeplist.push(decodeURIComponent(dpurl)); - if (lockfileVersion >= 9) { - // These are direct dependencies so cannot be optional - possibleOptionalDeps[decodeURIComponent(dpurl)] = false; - } - } - - dependenciesList.push({ - ref: decodeURIComponent(ppurl), - dependsOn: ddeplist, - }); - - // pnpm-lock.yaml contains more than root dependencies in importers - // TODO: verify if this is a good way to check. - // we do what we did above but for all the other components - for (const importedComponentName of Object.keys(yamlObj['importers'])) { - - const componentDeps = yamlObj['importers'][importedComponentName]['dependencies'] || {}; - const componentDevDeps = yamlObj['importers'][importedComponentName]['devDependencies'] || {}; - const optionalDeps = yamlObj['importers'][importedComponentName]['optionalDependencies'] || {}; - - let name = importedComponentName.split('/'); - let lastname = name[name.length -1]; - - let subpath = name.filter(part => part !== '.' && part !== '..').join('/'); - - // if component name is '.' continue loop - if (lastname == "."){ - continue; - } - - // TODO: verify as we had seen before with rush there can be ../name in pnpm-lock-yaml - let compPurl = new PackageURL( - "npm", - parentComponent.group, - parentComponent.name + "/" + lastname, - parentComponent.version, - null, - subpath, - ).toString(); - // Find the component optional dependencies - let comDepList = []; - for (const cdk of Object.keys(componentDeps)) { - let version = getVersionNumPnpm(componentDeps[cdk]); + if (yamlObj["importers"]) { + // In lock file version 9, direct dependencies is under importers + const rootDirectDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.dependencies || {} + : yamlObj.dependencies || {}; + const rootDevDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.devDependencies || {} + : {}; + const rootOptionalDeps = + lockfileVersion >= 9 + ? yamlObj.importers["."]?.optionalDependencies || {} + : {}; + const ddeplist = []; + // Find the root optional dependencies + for (const rdk of Object.keys(rootDevDeps)) { + const version = getVersionNumPnpm(rootDevDeps[rdk]); const dpurl = new PackageURL( "npm", "", - cdk, + rdk, version, null, null, ).toString(); - comDepList.push(decodeURIComponent(dpurl)) - // TODO: verify if this is possibleOptionalDeps false - possibleOptionalDeps[decodeURIComponent(dpurl)] = false; + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } - - dependenciesList.push({ - ref: decodeURIComponent(compPurl), - dependsOn: comDepList, - }); - - for (const cdk of Object.keys(componentDevDeps)) { - let version = getVersionNumPnpm(componentDevDeps[cdk]); + for (const rdk of Object.keys(rootOptionalDeps)) { + const version = getVersionNumPnpm(rootOptionalDeps[rdk]); const dpurl = new PackageURL( "npm", "", - cdk, + rdk, version, null, null, ).toString(); possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } - - for (const cdk of Object.keys(optionalDeps)) { - let version = getVersionNumPnpm(optionalDeps[cdk]); + for (const dk of Object.keys(rootDirectDeps)) { + const version = getVersionNumPnpm(rootDirectDeps[dk]); const dpurl = new PackageURL( "npm", "", - cdk, + dk, version, null, null, ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; + ddeplist.push(decodeURIComponent(dpurl)); + if (lockfileVersion >= 9) { + // These are direct dependencies so cannot be optional + possibleOptionalDeps[decodeURIComponent(dpurl)] = false; + } } - } - - } - catch(e){ - // Error may be due to yamlObj not being defined - if(DEBUG_MODE){ - console.log("Error while parsing pnpm lock file" + e); + dependenciesList.push({ + ref: decodeURIComponent(ppurl), + dependsOn: ddeplist, + }); + + // pnpm-lock.yaml contains more than root dependencies in importers + // we do what we did above but for all the other components + for (const importedComponentName of Object.keys(yamlObj["importers"])) { + const componentDeps = + yamlObj["importers"][importedComponentName]["dependencies"] || {}; + const componentDevDeps = + yamlObj["importers"][importedComponentName]["devDependencies"] || + {}; + const optionalDeps = + yamlObj["importers"][importedComponentName][ + "optionalDependencies" + ] || {}; + + const name = importedComponentName.split("/"); + const lastname = name[name.length - 1]; + + // let subpath = name.filter(part => part !== '.' && part !== '..').join('/'); + const subpath = name.join("/").replace("./", "").replace("../", ""); + + // if component name is '.' continue loop + if (lastname === ".") { + continue; + } + + const compPurl = new PackageURL( + "npm", + parentComponent.group, + `${parentComponent.name}/${lastname}`, + parentComponent.version, + null, + subpath, + ).toString(); + // Find the component optional dependencies + const comDepList = []; + for (const cdk of Object.keys(componentDeps)) { + const version = getVersionNumPnpm(componentDeps[cdk]); + const dpurl = new PackageURL( + "npm", + "", + cdk, + version, + null, + null, + ).toString(); + comDepList.push(decodeURIComponent(dpurl)); + + possibleOptionalDeps[decodeURIComponent(dpurl)] = false; + } + + dependenciesList.push({ + ref: decodeURIComponent(compPurl), + dependsOn: comDepList, + }); + + for (const cdk of Object.keys(componentDevDeps)) { + const version = getVersionNumPnpm(componentDevDeps[cdk]); + const dpurl = new PackageURL( + "npm", + "", + cdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; + } + + for (const cdk of Object.keys(optionalDeps)) { + const version = getVersionNumPnpm(optionalDeps[cdk]); + const dpurl = new PackageURL( + "npm", + "", + cdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; + } + } } } - } const packages = yamlObj.packages || {}; // snapshots is a new key under lockfile version 9 const snapshots = yamlObj.snapshots || {}; From cbc1f5919c8cdcc67ea44b164c4d5787a21dca0b Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Wed, 18 Sep 2024 15:26:59 +0530 Subject: [PATCH 08/11] use replaceAll instead of replace Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index 496133332..17220eba4 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1861,7 +1861,10 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { const lastname = name[name.length - 1]; // let subpath = name.filter(part => part !== '.' && part !== '..').join('/'); - const subpath = name.join("/").replace("./", "").replace("../", ""); + const subpath = name + .join("/") + .replaceAll("../", "") + .replaceAll("./", ""); // if component name is '.' continue loop if (lastname === ".") { From d4c3218ba43c2f269a84601f948a48ca0a78322f Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sat, 21 Sep 2024 17:27:04 +0530 Subject: [PATCH 09/11] fix type Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index 17220eba4..5747c95dd 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1727,7 +1727,7 @@ function _markTreeOptional( } function getVersionNumPnpm(depPkg) { - let version; + let version = depPkg; if (typeof version === "object" && depPkg.version) { version = depPkg.version; } @@ -1852,7 +1852,7 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { const componentDevDeps = yamlObj["importers"][importedComponentName]["devDependencies"] || {}; - const optionalDeps = + const componentOptionalDeps = yamlObj["importers"][importedComponentName][ "optionalDependencies" ] || {}; @@ -1914,8 +1914,8 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } - for (const cdk of Object.keys(optionalDeps)) { - const version = getVersionNumPnpm(optionalDeps[cdk]); + for (const cdk of Object.keys(componentOptionalDeps)) { + const version = getVersionNumPnpm(componentOptionalDeps[cdk]); const dpurl = new PackageURL( "npm", "", From a4e9b17d902842c46bc8ad2b805179a7df273e2d Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sun, 22 Sep 2024 21:40:22 +0530 Subject: [PATCH 10/11] add suggested changes except testcase Signed-off-by: Aryan Rajoria --- lib/helpers/utils.js | 240 +++++++++++++++++++++---------------------- 1 file changed, 119 insertions(+), 121 deletions(-) diff --git a/lib/helpers/utils.js b/lib/helpers/utils.js index 5747c95dd..02b765139 100644 --- a/lib/helpers/utils.js +++ b/lib/helpers/utils.js @@ -1749,6 +1749,8 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { const dependenciesList = []; // For lockfile >= 9, we need to track dev and optional packages manually // See: #1163 + // Moreover, we have have changed >= 9 for >= 6 + // See: discussion #1359 const possibleOptionalDeps = {}; const dependenciesMap = {}; let ppurl = ""; @@ -1781,151 +1783,147 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) { // ignore parse errors } // This logic matches the pnpm list command to include only direct dependencies - if (ppurl !== "") { - if (yamlObj["importers"]) { - // In lock file version 9, direct dependencies is under importers - const rootDirectDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.dependencies || {} - : yamlObj.dependencies || {}; - const rootDevDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.devDependencies || {} - : {}; - const rootOptionalDeps = - lockfileVersion >= 9 - ? yamlObj.importers["."]?.optionalDependencies || {} - : {}; - const ddeplist = []; - // Find the root optional dependencies - for (const rdk of Object.keys(rootDevDeps)) { - const version = getVersionNumPnpm(rootDevDeps[rdk]); + if (ppurl !== "" && yamlObj["importers"]) { + // In lock file version 9, direct dependencies is under importers + const rootDirectDeps = + lockfileVersion >= 6 + ? yamlObj.importers["."]?.dependencies || {} + : yamlObj.dependencies || {}; + const rootDevDeps = + lockfileVersion >= 6 + ? yamlObj.importers["."]?.devDependencies || {} + : {}; + const rootOptionalDeps = + lockfileVersion >= 6 + ? yamlObj.importers["."]?.optionalDependencies || {} + : {}; + const ddeplist = []; + // Find the root optional dependencies + for (const rdk of Object.keys(rootDevDeps)) { + const version = getVersionNumPnpm(rootDevDeps[rdk]); + const dpurl = new PackageURL( + "npm", + "", + rdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; + } + for (const rdk of Object.keys(rootOptionalDeps)) { + const version = getVersionNumPnpm(rootOptionalDeps[rdk]); + const dpurl = new PackageURL( + "npm", + "", + rdk, + version, + null, + null, + ).toString(); + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; + } + for (const dk of Object.keys(rootDirectDeps)) { + const version = getVersionNumPnpm(rootDirectDeps[dk]); + const dpurl = new PackageURL( + "npm", + "", + dk, + version, + null, + null, + ).toString(); + ddeplist.push(decodeURIComponent(dpurl)); + if (lockfileVersion >= 6) { + // These are direct dependencies so cannot be optional + possibleOptionalDeps[decodeURIComponent(dpurl)] = false; + } + } + + dependenciesList.push({ + ref: decodeURIComponent(ppurl), + dependsOn: ddeplist, + }); + + // pnpm-lock.yaml contains more than root dependencies in importers + // we do what we did above but for all the other components + for (const importedComponentName of Object.keys(yamlObj["importers"])) { + const componentDeps = + yamlObj["importers"][importedComponentName]["dependencies"] || {}; + const componentDevDeps = + yamlObj["importers"][importedComponentName]["devDependencies"] || {}; + const componentOptionalDeps = + yamlObj["importers"][importedComponentName]["optionalDependencies"] || + {}; + + const name = importedComponentName.split("/"); + const lastname = name[name.length - 1]; + + // let subpath = name.filter(part => part !== '.' && part !== '..').join('/'); + const subpath = name + .join("/") + .replaceAll("../", "") + .replaceAll("./", ""); + + // if component name is '.' continue loop + if (lastname === ".") { + continue; + } + + const compPurl = new PackageURL( + "npm", + parentComponent.group, + `${parentComponent.name}/${lastname}`, + parentComponent.version, + null, + subpath, + ).toString(); + // Find the component optional dependencies + const comDepList = []; + for (const cdk of Object.keys(componentDeps)) { + const version = getVersionNumPnpm(componentDeps[cdk]); const dpurl = new PackageURL( "npm", "", - rdk, + cdk, version, null, null, ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; + comDepList.push(decodeURIComponent(dpurl)); + + possibleOptionalDeps[decodeURIComponent(dpurl)] = false; } - for (const rdk of Object.keys(rootOptionalDeps)) { - const version = getVersionNumPnpm(rootOptionalDeps[rdk]); + + dependenciesList.push({ + ref: decodeURIComponent(compPurl), + dependsOn: comDepList, + }); + + for (const cdk of Object.keys(componentDevDeps)) { + const version = getVersionNumPnpm(componentDevDeps[cdk]); const dpurl = new PackageURL( "npm", "", - rdk, + cdk, version, null, null, ).toString(); possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } - for (const dk of Object.keys(rootDirectDeps)) { - const version = getVersionNumPnpm(rootDirectDeps[dk]); + + for (const cdk of Object.keys(componentOptionalDeps)) { + const version = getVersionNumPnpm(componentOptionalDeps[cdk]); const dpurl = new PackageURL( "npm", "", - dk, + cdk, version, null, null, ).toString(); - ddeplist.push(decodeURIComponent(dpurl)); - if (lockfileVersion >= 9) { - // These are direct dependencies so cannot be optional - possibleOptionalDeps[decodeURIComponent(dpurl)] = false; - } - } - - dependenciesList.push({ - ref: decodeURIComponent(ppurl), - dependsOn: ddeplist, - }); - - // pnpm-lock.yaml contains more than root dependencies in importers - // we do what we did above but for all the other components - for (const importedComponentName of Object.keys(yamlObj["importers"])) { - const componentDeps = - yamlObj["importers"][importedComponentName]["dependencies"] || {}; - const componentDevDeps = - yamlObj["importers"][importedComponentName]["devDependencies"] || - {}; - const componentOptionalDeps = - yamlObj["importers"][importedComponentName][ - "optionalDependencies" - ] || {}; - - const name = importedComponentName.split("/"); - const lastname = name[name.length - 1]; - - // let subpath = name.filter(part => part !== '.' && part !== '..').join('/'); - const subpath = name - .join("/") - .replaceAll("../", "") - .replaceAll("./", ""); - - // if component name is '.' continue loop - if (lastname === ".") { - continue; - } - - const compPurl = new PackageURL( - "npm", - parentComponent.group, - `${parentComponent.name}/${lastname}`, - parentComponent.version, - null, - subpath, - ).toString(); - // Find the component optional dependencies - const comDepList = []; - for (const cdk of Object.keys(componentDeps)) { - const version = getVersionNumPnpm(componentDeps[cdk]); - const dpurl = new PackageURL( - "npm", - "", - cdk, - version, - null, - null, - ).toString(); - comDepList.push(decodeURIComponent(dpurl)); - - possibleOptionalDeps[decodeURIComponent(dpurl)] = false; - } - - dependenciesList.push({ - ref: decodeURIComponent(compPurl), - dependsOn: comDepList, - }); - - for (const cdk of Object.keys(componentDevDeps)) { - const version = getVersionNumPnpm(componentDevDeps[cdk]); - const dpurl = new PackageURL( - "npm", - "", - cdk, - version, - null, - null, - ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; - } - - for (const cdk of Object.keys(componentOptionalDeps)) { - const version = getVersionNumPnpm(componentOptionalDeps[cdk]); - const dpurl = new PackageURL( - "npm", - "", - cdk, - version, - null, - null, - ).toString(); - possibleOptionalDeps[decodeURIComponent(dpurl)] = true; - } + possibleOptionalDeps[decodeURIComponent(dpurl)] = true; } } } From 4af2c71c72541ae19d51a34bc8b596ac547bf348 Mon Sep 17 00:00:00 2001 From: Aryan Rajoria Date: Sun, 22 Sep 2024 22:17:01 +0530 Subject: [PATCH 11/11] add test file and test cases Signed-off-by: Aryan Rajoria --- lib/helpers/utils.test.js | 33 ++++++++ test/data/pnpm-lock6b.yaml | 154 +++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 test/data/pnpm-lock6b.yaml diff --git a/lib/helpers/utils.test.js b/lib/helpers/utils.test.js index ac9cda88d..adf32010e 100644 --- a/lib/helpers/utils.test.js +++ b/lib/helpers/utils.test.js @@ -3159,6 +3159,39 @@ test("parsePnpmLock", async () => { }, }, }); + // Test case to see if parsePnpmLock is finding all root deps + const dummpyParent = { + name: "rush", + group: "", + purl: "pkg:npm/rush", + type: "application", + "bom-ref": "pkg:npm/rush", + }; + parsedList = await parsePnpmLock( + "./test/data/pnpm-lock6b.yaml", + dummpyParent, + ); + expect(parsedList.pkgList.length).toEqual(17); + // this is due to additions projects defined in importers section of pnpm-lock.yaml + expect(parsedList.dependenciesList.length).toEqual(21); + const mainRootDependency = parsedList.dependenciesList.find( + (obj) => obj["ref"] === "pkg:npm/rush", + ); + const myAppRootDependency = parsedList.dependenciesList.find( + (obj) => obj["ref"] === "pkg:npm/rush/my-app#apps/my-app", + ); + const myControlsRootDependency = parsedList.dependenciesList.find( + (obj) => obj["ref"] === "pkg:npm/rush/my-controls#libraries/my-controls", + ); + const myToolChainRootDependency = parsedList.dependenciesList.find( + (obj) => obj["ref"] === "pkg:npm/rush/my-toolchain#tools/my-toolchain", + ); + + expect(mainRootDependency["dependsOn"].length).toEqual(0); + expect(myAppRootDependency["dependsOn"].length).toEqual(2); + expect(myControlsRootDependency["dependsOn"].length).toEqual(0); + expect(myToolChainRootDependency["dependsOn"].length).toEqual(1); + parsedList = await parsePnpmLock("./test/data/pnpm-lock9a.yaml", { name: "pnpm9", purl: "pkg:npm/pnpm9@1.0.0", diff --git a/test/data/pnpm-lock6b.yaml b/test/data/pnpm-lock6b.yaml new file mode 100644 index 000000000..a188688ff --- /dev/null +++ b/test/data/pnpm-lock6b.yaml @@ -0,0 +1,154 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +importers: + + .: {} + + ../../apps/my-app: + dependencies: + my-controls: + specifier: workspace:^1.0.0 + version: link:../../libraries/my-controls + whatwg-fetch: + specifier: ^3.6.2 + version: 3.6.2 + devDependencies: + my-toolchain: + specifier: workspace:^1.0.0 + version: link:../../tools/my-toolchain + typescript: + specifier: ^3.0.3 + version: 3.0.3 + + ../../libraries/my-controls: + devDependencies: + my-toolchain: + specifier: workspace:^1.0.0 + version: link:../../tools/my-toolchain + typescript: + specifier: ^3.0.3 + version: 3.0.3 + + ../../tools/my-toolchain: + dependencies: + colors: + specifier: ^1.4.0 + version: 1.4.0 + devDependencies: + '@types/node': + specifier: 16.11.47 + version: 16.11.47 + rimraf: + specifier: ^2.7.1 + version: 2.7.1 + typescript: + specifier: ^4.7.4 + version: 4.7.4 + +packages: + + /@types/node@16.11.47: + resolution: {integrity: sha512-fpP+jk2zJ4VW66+wAMFoBJlx1bxmBKx4DUFf68UHgdGCOuyUTDlLWqsaNPJh7xhNDykyJ9eIzAygilP/4WoN8g==} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + dev: false + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /typescript@3.0.3: + resolution: {integrity: sha512-kk80vLW9iGtjMnIv11qyxLqZm20UklzuR2tL0QAnDIygIUIemcZMxlMWudl9OOt76H3ntVzcTiddQ1/pAAJMYg==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /typescript@4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /whatwg-fetch@3.6.2: + resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==} + dev: false + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + +time: + /@types/node@16.11.47: '2022-07-30T21:03:20.126Z' + /colors@1.4.0: '2019-09-22T23:46:07.522Z' + /rimraf@2.7.1: '2019-08-14T16:53:32.844Z' + /typescript@3.0.3: '2018-08-29T21:59:20.079Z' + /typescript@4.7.4: '2022-06-17T18:21:36.833Z' + /whatwg-fetch@3.6.2: '2021-02-27T18:45:53.796Z' \ No newline at end of file