diff --git a/src/domain/getPackageDependencies.ts b/src/domain/getPackageDependencies.ts index 765afaf..c7838d0 100644 --- a/src/domain/getPackageDependencies.ts +++ b/src/domain/getPackageDependencies.ts @@ -6,6 +6,7 @@ import { PackageVersion, } from "./types"; import { maxSatisfying } from "semver"; +import axios from "axios"; /** * A function which resolves the direct dependencies of a @@ -25,7 +26,7 @@ export async function getPackageDependencies( packageName: PackageName, packageVersion: PackageVersion, getPackage: PackageGetter, -): Promise> { +): Promise> { const npmPackage: NPMPackage = await getPackage(packageName); const packageForVersion = npmPackage.versions[packageVersion]; @@ -34,15 +35,48 @@ export async function getPackageDependencies( } const dependenciesWithRange = packageForVersion?.dependencies ?? {}; - const dependencies: Record = {}; + const dependencies: Record = {}; for (const [dependencyName, range] of Object.entries(dependenciesWithRange)) { - const dependency: NPMPackage = await getPackage(dependencyName); - - const maxSatisfyingVersion = - maxSatisfying(Object.keys(dependency.versions), range) ?? range; - dependencies[dependencyName] = maxSatisfyingVersion; + dependencies[dependencyName] = await resolveDependenciesRecursively( + dependencyName, + range, + ); } return dependencies; } + +async function resolveDependenciesRecursively( + name: string, + range: string, +): Promise { + const npmPackage = await ( + await axios.get(`https://registry.npmjs.org/${name}`) + ).data; + + const maxSatisfyingVersion = maxSatisfying( + Object.keys(npmPackage.versions), + range, + ) as string; + + if (!npmPackage.versions[maxSatisfyingVersion]) { + throw new Error(); + } + + const dependencies: Record = {}; + for (const [name, range] of Object.entries( + npmPackage.versions[maxSatisfyingVersion].dependencies ?? {}, + )) { + // eslint-disable-next-line + // @ts-ignore + dependencies[name] = await resolveDependenciesRecursively(name, range); + } + + return { version: maxSatisfyingVersion, dependencies }; +} + +interface Package { + version: string; + dependencies: Record; +} diff --git a/tests/routes/getPackage.integration.test.ts b/tests/routes/getPackage.integration.test.ts index e4ad0a8..8364fcd 100644 --- a/tests/routes/getPackage.integration.test.ts +++ b/tests/routes/getPackage.integration.test.ts @@ -33,7 +33,7 @@ describe("/package/:packageName/:packageVersion endpoint", () => { server.close(); }); - it("responds with list of dependencies", async () => { + it("responds with a tree of dependencies", async () => { const packageName = "react"; const version = "16.3.0"; const address = `http://localhost:${port}/package/${packageName}/${version}`; @@ -42,7 +42,12 @@ describe("/package/:packageName/:packageVersion endpoint", () => { const expectedResponse = { name: "react", version: "16.3.0", - dependencies: { "loose-envify": "1.1.0" }, + dependencies: { + "loose-envify": { + version: "1.1.0", + dependencies: { "js-tokens": { version: "1.0.3", dependencies: {} } }, + }, + }, }; expect(response.data).toEqual(expectedResponse);