diff --git a/src/base.ts b/src/base.ts index 5c60f0761..15ece4723 100644 --- a/src/base.ts +++ b/src/base.ts @@ -7,6 +7,7 @@ import { z } from "zod"; import { readAccess } from "./options/readAccess.js"; import { readAllContributors } from "./options/readAllContributors.js"; import { readAuthor } from "./options/readAuthor.js"; +import { readBin } from "./options/readBin.js"; import { readDescription } from "./options/readDescription.js"; import { readDocumentation } from "./options/readDocumentation.js"; import { readEmailFromCodeOfConduct } from "./options/readEmailFromCodeOfConduct.js"; @@ -181,7 +182,7 @@ export const base = createBase({ await readAuthor(getPackageAuthor, getNpmDefaults, options.owner), ); - const getBin = lazyValue(async () => (await getPackageData()).bin); + const getBin = lazyValue(async () => await readBin(getPackageData)); const getEmoji = lazyValue(async () => await readEmoji(getDescription)); diff --git a/src/options/readBin.test.ts b/src/options/readBin.test.ts new file mode 100644 index 000000000..c91bf01ba --- /dev/null +++ b/src/options/readBin.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; + +import { readBin } from "./readBin.js"; + +describe(readBin, () => { + it("resolves with undefined when package data has no bin", async () => { + const getPackageData = () => Promise.resolve({}); + + const actual = await readBin(getPackageData); + + expect(actual).toBe(undefined); + }); + + it("resolves with a trimmed string when the package data has a string bin", async () => { + const getPackageData = () => + Promise.resolve({ + bin: "./index.js", + }); + + const actual = await readBin(getPackageData); + + expect(actual).toBe("index.js"); + }); + + it("resolves with an object of trimmed bins when the package data has a string bin", async () => { + const getPackageData = () => + Promise.resolve({ + bin: { + absolute: "index.js", + relative: "./index.js", + }, + }); + + const actual = await readBin(getPackageData); + + expect(actual).toEqual({ + absolute: "index.js", + relative: "index.js", + }); + }); +}); diff --git a/src/options/readBin.ts b/src/options/readBin.ts new file mode 100644 index 000000000..b097700c9 --- /dev/null +++ b/src/options/readBin.ts @@ -0,0 +1,17 @@ +import { PartialPackageData } from "../types.js"; +import { trimPrecedingSlash } from "../utils/trimPrecedingSlash.js"; + +export async function readBin( + getPackageData: () => Promise, +) { + const { bin } = await getPackageData(); + + return typeof bin === "object" + ? (Object.fromEntries( + Object.entries(bin).map(([key, value]) => [ + key, + trimPrecedingSlash(value), + ]), + ) as typeof bin) + : trimPrecedingSlash(bin); +} diff --git a/src/utils/trimPrecedingSlash.test.ts b/src/utils/trimPrecedingSlash.test.ts new file mode 100644 index 000000000..31b1e8791 --- /dev/null +++ b/src/utils/trimPrecedingSlash.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, test } from "vitest"; + +import { trimPrecedingSlash } from "./trimPrecedingSlash.js"; + +describe(trimPrecedingSlash, () => { + test.each([ + [undefined, undefined], + ["", ""], + ["a", "a"], + ["./a", "a"], + ])("%s becomes %s", (input, expected) => { + const actual = trimPrecedingSlash(input); + expect(actual).toBe(expected); + }); +}); diff --git a/src/utils/trimPrecedingSlash.ts b/src/utils/trimPrecedingSlash.ts new file mode 100644 index 000000000..8323fd398 --- /dev/null +++ b/src/utils/trimPrecedingSlash.ts @@ -0,0 +1,3 @@ +export function trimPrecedingSlash(filePath: string | undefined) { + return filePath?.replace(/^\.\//, ""); +}