diff --git a/package.json b/package.json index 5fcbf11a..16032f1a 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "devDependencies": { "@types/eslint": "^8.37.0", "@types/node": "^20.3.1", + "@types/npm-user-packages": "^3.0.1", "@typescript-eslint/eslint-plugin": "^5.59.5", "@typescript-eslint/parser": "^5.59.5", "@vitest/coverage-istanbul": "^0.31.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 332978e5..f4671040 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,9 @@ devDependencies: '@types/node': specifier: ^20.3.1 version: 20.3.1 + '@types/npm-user-packages': + specifier: ^3.0.1 + version: 3.0.1 '@typescript-eslint/eslint-plugin': specifier: ^5.59.5 version: 5.59.5(@typescript-eslint/parser@5.59.5)(eslint@8.40.0)(typescript@5.0.4) @@ -1188,6 +1191,10 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/npm-user-packages@3.0.1: + resolution: {integrity: sha512-EPLHtduuKXrEPkmSM5NB7t5n7vxqh/Qz+UnvrdE4GPAR0dNR6W8i2rqdP8LYcDAFCuIhaSFocKBW5T21OXv0TA==} + dev: true + /@types/semver@7.3.13: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true diff --git a/src/fakes.ts b/src/fakes.ts index 9f613502..180e8474 100644 --- a/src/fakes.ts +++ b/src/fakes.ts @@ -1,10 +1,23 @@ -import { UserPackageData } from "./createUserPackagesFilter.js"; +import { PackageData } from "npm-user-packages"; -export const createFakePackageData = (overrides?: Partial) => +export const createFakePackageData = (overrides?: Partial) => ({ - author: { name: "" }, - date: new Date().toString(), + author: { + name: "", + }, + date: "", + description: "", + keywords: [], + links: { + npm: "", + }, maintainers: [], - publisher: { email: "", username: "" }, + name: "", + publisher: { + email: "", + username: "", + }, + scope: "", + version: "", ...overrides, - } satisfies UserPackageData); + } satisfies PackageData); diff --git a/src/getPackageEstimates.ts b/src/getPackageEstimates.ts index a55f80d7..79eae668 100644 --- a/src/getPackageEstimates.ts +++ b/src/getPackageEstimates.ts @@ -1,5 +1,3 @@ -import { PackageEstimate } from "./types.js"; - interface PackageEstimateData { estimated_money: string; lifted: boolean; @@ -7,9 +5,7 @@ interface PackageEstimateData { platform: "npm"; } -export async function getPackageEstimates( - packageNames: string[] -): Promise { +export async function getPackageEstimates(packageNames: string[]) { const response = await fetch( "https://tidelift.com/api/depci/estimate/bulk_estimates", { diff --git a/src/reporters/jsonReporter.test.ts b/src/reporters/jsonReporter.test.ts index fdfb70b6..4f4162cf 100644 --- a/src/reporters/jsonReporter.test.ts +++ b/src/reporters/jsonReporter.test.ts @@ -1,20 +1,23 @@ import { describe, expect, it, vi } from "vitest"; +import { createFakePackageData } from "../fakes.js"; +import { EstimatedPackage } from "../types.js"; import { jsonReporter } from "./jsonReporter.js"; describe("jsonReporter", () => { it("directly logs from JSON.stringify", () => { const logger = vi.spyOn(console, "log").mockImplementation(() => undefined); - const packageEstimates = [ + const estimatedPackages = [ { + data: createFakePackageData(), estimatedMoney: 12.34, lifted: false, name: "abc123", }, - ]; + ] satisfies EstimatedPackage[]; - jsonReporter(packageEstimates); + jsonReporter(estimatedPackages); - expect(logger).toHaveBeenCalledWith(JSON.stringify(packageEstimates)); + expect(logger).toHaveBeenCalledWith(JSON.stringify(estimatedPackages)); }); }); diff --git a/src/reporters/jsonReporter.ts b/src/reporters/jsonReporter.ts index df0b36a2..6aef97e4 100644 --- a/src/reporters/jsonReporter.ts +++ b/src/reporters/jsonReporter.ts @@ -1,5 +1,5 @@ -import { PackageEstimate } from "../types.js"; +import { EstimatedPackage } from "../types.js"; -export function jsonReporter(packageEstimates: PackageEstimate[]) { - console.log(JSON.stringify(packageEstimates)); +export function jsonReporter(estimatedPackages: EstimatedPackage[]) { + console.log(JSON.stringify(estimatedPackages)); } diff --git a/src/reporters/textReporter.test.ts b/src/reporters/textReporter.test.ts index 1423a171..251e5e78 100644 --- a/src/reporters/textReporter.test.ts +++ b/src/reporters/textReporter.test.ts @@ -1,6 +1,7 @@ import chalk from "chalk"; import { describe, expect, it, vi } from "vitest"; +import { createFakePackageData } from "../fakes.js"; import { textReporter } from "./textReporter.js"; describe("textReporter", () => { @@ -9,6 +10,7 @@ describe("textReporter", () => { textReporter([ { + data: createFakePackageData(), estimatedMoney: 12.34, lifted: true, name: "abc123", @@ -25,6 +27,7 @@ describe("textReporter", () => { textReporter([ { + data: createFakePackageData(), estimatedMoney: 12.34, lifted: false, name: "abc123", diff --git a/src/reporters/textReporter.ts b/src/reporters/textReporter.ts index e59045b5..c68cf55a 100644 --- a/src/reporters/textReporter.ts +++ b/src/reporters/textReporter.ts @@ -1,25 +1,25 @@ import chalk from "chalk"; -import { PackageEstimate } from "../types.js"; +import { EstimatedPackage } from "../types.js"; -export function textReporter(packageEstimates: PackageEstimate[]) { - for (const packageEstimate of packageEstimates) { +export function textReporter(estimatedPackages: EstimatedPackage[]) { + for (const estimatedPackage of estimatedPackages) { const currency = new Intl.NumberFormat("en-US", { currency: "USD", style: "currency", - }).format(packageEstimate.estimatedMoney); + }).format(estimatedPackage.estimatedMoney); - if (packageEstimate.lifted) { + if (estimatedPackage.lifted) { console.log( chalk.gray( - `✅ ${packageEstimate.name} is already lifted for ${currency}/mo.` + `✅ ${estimatedPackage.name} is already lifted for ${currency}/mo.` ) ); } else { console.log( [ chalk.cyan(`👉 `), - chalk.cyanBright(packageEstimate.name), + chalk.cyanBright(estimatedPackage.name), ` is not yet lifted, but is estimated for `, chalk.cyanBright(`${currency}/mo`), `.`, diff --git a/src/tideliftMeUp.test.ts b/src/tideliftMeUp.test.ts index ad2a8347..330336ca 100644 --- a/src/tideliftMeUp.test.ts +++ b/src/tideliftMeUp.test.ts @@ -21,7 +21,9 @@ vi.mock("./getNpmWhoami.js", () => ({ }, })); -vi.mock("./getPackageEstimates.js"); +vi.mock("./getPackageEstimates.js", () => ({ + getPackageEstimates: () => [], +})); describe("tideliftMeUp", () => { it("throws an error when --username isn't provided and getNpmWhoami returns undefined", async () => { diff --git a/src/tideliftMeUp.ts b/src/tideliftMeUp.ts index 33ad6235..de9d2df5 100644 --- a/src/tideliftMeUp.ts +++ b/src/tideliftMeUp.ts @@ -2,7 +2,7 @@ import { createUserPackagesFilter } from "./createUserPackagesFilter.js"; import { getNpmUserPackages } from "./getNpmUserPackages.js"; import { getNpmWhoami } from "./getNpmWhoami.js"; import { getPackageEstimates } from "./getPackageEstimates.js"; -import { PackageOwnership } from "./types.js"; +import { EstimatedPackage, PackageOwnership } from "./types.js"; export interface TideliftMeUpSettings { ownership?: PackageOwnership[]; @@ -14,7 +14,7 @@ export async function tideliftMeUp({ ownership = ["author", "publisher"], since = getTwoYearsAgo(), username, -}: TideliftMeUpSettings = {}) { +}: TideliftMeUpSettings = {}): Promise { username ??= await getNpmWhoami(); if (!username) { throw new Error("Either log in to npm or provide a `username`."); @@ -23,10 +23,18 @@ export async function tideliftMeUp({ const userPackages = (await getNpmUserPackages(username)).filter( createUserPackagesFilter({ ownership, since: new Date(since), username }) ); + const userPackagesByName = Object.fromEntries( + userPackages.map((userPackage) => [userPackage.name, userPackage]) + ); - return await getPackageEstimates( + const packageEstimates = await getPackageEstimates( userPackages.map((userPackage) => userPackage.name) ); + + return packageEstimates.map((packageEstimate) => ({ + ...packageEstimate, + data: userPackagesByName[packageEstimate.name], + })); } function getTwoYearsAgo() { diff --git a/src/types.ts b/src/types.ts index a792c842..efd33807 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,7 @@ -export interface PackageEstimate { +import { PackageData } from "npm-user-packages"; + +export interface EstimatedPackage { + data: PackageData; estimatedMoney: number; lifted: boolean; name: string;