diff --git a/__snapshots__/depWalker.spec.js.snapshot.js b/__snapshots__/depWalker.spec.js.snapshot.js index 5408460..d18663a 100644 --- a/__snapshots__/depWalker.spec.js.snapshot.js +++ b/__snapshots__/depWalker.spec.js.snapshot.js @@ -85,7 +85,12 @@ exports['walk @slimio/is 1'] = { ] }, "gitUrl": null, - "integrity": "c9781c55ab750e58bed9ce2560581ff4087b8c3129462543fa6fee4e717ba2a9" + "integrity": "c9781c55ab750e58bed9ce2560581ff4087b8c3129462543fa6fee4e717ba2a9", + "links": { + "npm": "https://www.npmjs.com/package/@slimio/is/v/1.5.1", + "homepage": "https://github.com/SlimIO/is#readme", + "repository": "https://github.com/SlimIO/is" + } } }, "vulnerabilities": [], diff --git a/src/depWalker.js b/src/depWalker.js index dd13dff..0c59a9a 100644 --- a/src/depWalker.js +++ b/src/depWalker.js @@ -293,7 +293,7 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) { logger.tick(ScannerLoggerEvents.analysis.tree); // There is no need to fetch 'N' times the npm metadata for the same package. - if (fetchedMetadataPackages.has(name)) { + if (fetchedMetadataPackages.has(name) || !current.versions[version].existOnRemoteRegistry) { logger.tick(ScannerLoggerEvents.analysis.registry); } else { diff --git a/src/npmRegistry.js b/src/npmRegistry.js index d7898eb..cde9750 100644 --- a/src/npmRegistry.js +++ b/src/npmRegistry.js @@ -6,7 +6,7 @@ import semver from "semver"; import { packument, packumentVersion } from "@nodesecure/npm-registry-sdk"; // Import Internal Dependencies -import { parseAuthor } from "./utils/index.js"; +import { parseAuthor, getLinks } from "./utils/index.js"; export async function manifestMetadata(name, version, metadata) { try { @@ -85,6 +85,7 @@ export async function packageMetadata(name, version, options) { } } + Object.assign(ref.versions[version], { links: getLinks(pkg.versions[version]) }); Object.assign(ref.metadata, metadata); } catch { diff --git a/src/tarball.js b/src/tarball.js index fc587b1..24f7835 100644 --- a/src/tarball.js +++ b/src/tarball.js @@ -67,6 +67,10 @@ export async function scanDirOrArchive(name, version, options) { }); await timers.setImmediate(); } + else { + // Set links to an empty object because theses are generated only for NPM tarballs + Object.assign(ref, { links: {} }); + } // Read the package.json at the root of the directory or archive. const { diff --git a/src/utils/getLinks.js b/src/utils/getLinks.js new file mode 100644 index 0000000..65dc604 --- /dev/null +++ b/src/utils/getLinks.js @@ -0,0 +1,31 @@ +// CONSTANTS +const kVCSHosts = new Set(["github.com", "gitlab.com"]); + +function getVCSRepositoryURL(link) { + try { + const url = new URL(link); + const { hostname, pathname } = url; + + if (kVCSHosts.has(hostname) === false) { + return null; + } + + const [owner, repo] = pathname.split("/").filter(Boolean).map((curr) => curr.replace(".git", "")); + + return `https://${hostname}/${owner}/${repo}`; + } + catch { + return null; + } +} + +export function getLinks(pkg) { + const homepage = pkg.homepage || null; + const repositoryUrl = pkg.repository?.url || null; + + return { + npm: `https://www.npmjs.com/package/${pkg.name}/v/${pkg.version}`, + homepage, + repository: getVCSRepositoryURL(homepage) ?? getVCSRepositoryURL(repositoryUrl) + }; +} diff --git a/src/utils/index.js b/src/utils/index.js index 01688df..6080884 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -11,6 +11,7 @@ export * from "./analyzeDependencies.js"; export * from "./booleanToFlags.js"; export * from "./addMissingVersionFlags.js"; export * from "./parseManifestAuthor.js"; +export * from "./getLinks.js"; export const NPM_TOKEN = typeof process.env.NODE_SECURE_TOKEN === "string" ? { token: process.env.NODE_SECURE_TOKEN } : diff --git a/test/npmRegistry.spec.js b/test/npmRegistry.spec.js index 0140b9d..b69eadf 100644 --- a/test/npmRegistry.spec.js +++ b/test/npmRegistry.spec.js @@ -65,4 +65,33 @@ test("registry.packageMetadata", async() => { assert.ok(ref.metadata.hasManyPublishers); assert.ok(typeof ref.metadata.publishedCount === "number"); assert.ok(is.date(new Date(ref.metadata.lastUpdateAt))); + + assert.deepEqual(ref.versions["1.5.0"].links, { + npm: "https://www.npmjs.com/package/@slimio/is/v/1.5.0", + homepage: "https://github.com/SlimIO/is#readme", + repository: "https://github.com/SlimIO/is" + }); +}); + +test("registry.packageMetadata should find GitLab links", async() => { + const ref = { + metadata: {}, + versions: { + "71.2.0": { + flags: [] + } + } + }; + const logger = new Logger().start("registry"); + + await registry.packageMetadata("@gitlab/ui", "71.2.0", { + ref, + logger + }); + + assert.deepEqual(ref.versions["71.2.0"].links, { + npm: "https://www.npmjs.com/package/@gitlab/ui/v/71.2.0", + homepage: "https://gitlab.com/gitlab-org/gitlab-ui#readme", + repository: "https://gitlab.com/gitlab-org/gitlab-ui" + }); }); diff --git a/types/scanner.d.ts b/types/scanner.d.ts index bcdbb51..fd98b9d 100644 --- a/types/scanner.d.ts +++ b/types/scanner.d.ts @@ -40,6 +40,15 @@ declare namespace Scanner { at: string; } + export interface DependencyLinks { + /** NPM Registry page */ + npm: string; + /** Homepage URL */ + homepage?: string; + /** VCS repository URL */ + repository?: string; + } + export interface DependencyVersion { /** Id of the package (useful for usedBy relation) */ id: number; @@ -111,6 +120,7 @@ declare namespace Scanner { * (Not supported on GIT dependency) */ integrity?: string; + links: DependencyLinks; } export interface Dependency {