Skip to content

Commit

Permalink
feat: infer base from package json scripts during migration (#993)
Browse files Browse the repository at this point in the history
<!-- 👋 Hi, thanks for sending a PR to create-typescript-app! 💖.
Please fill out all fields below and make sure each item is true and [x]
checked.
Otherwise we may not be able to review your PR. -->

## PR Checklist

- [x] Addresses an existing open issue: fixes #933
- [x] That issue was marked as [`status: accepting
prs`](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22)
- [x] Steps in
[CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/create-typescript-app/blob/main/.github/CONTRIBUTING.md)
were taken

## Overview

Add a naive solution for checking the existing `package.json` during
migration to infer the base.

🐸

---------

Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>
  • Loading branch information
GV14982 and JoshuaKGoldberg authored Oct 31, 2023
1 parent c7217b7 commit 20afaf4
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/shared/options/getBase.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { describe, expect, it, vi } from "vitest";

import { getBase } from "./getBase.js";

const mockReadPackageData = vi.fn();
vi.mock("../packages.js", () => ({
get readPackageData() {
return mockReadPackageData;
},
}));

describe("getBase", () => {
it("should return minimum with minimum scripts", async () => {
mockReadPackageData.mockImplementationOnce(() =>
Promise.resolve({
scripts: {
build: "build",
lint: "lint",
test: "test",
},
}),
);

expect(await getBase()).toBe("minimum");
});
it("should return common with common scripts", async () => {
mockReadPackageData.mockImplementationOnce(() =>
Promise.resolve({
scripts: {
build: "build",
lint: "lint",
"lint:knip": "knip",
test: "test",
},
}),
);

expect(await getBase()).toBe("common");
});
it("should return everything with everything scripts", async () => {
mockReadPackageData.mockImplementationOnce(() =>
Promise.resolve({
scripts: {
build: "build",
lint: "lint",
"lint:knip": "knip",
"lint:md": "md",
"lint:package-json": "package-json",
"lint:packages": "packages",
test: "test",
},
}),
);

expect(await getBase()).toBe("everything");
});
});
34 changes: 34 additions & 0 deletions src/shared/options/getBase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { readPackageData } from "../packages.js";
import { OptionsBase } from "../types.js";

const commonScripts = new Set(["lint:knip", "should-semantic-release", "test"]);

const everythingScripts = new Set([
"lint:md",
"lint:package-json",
"lint:packages",
"lint:spelling",
]);
export async function getBase(): Promise<OptionsBase> {
const scripts = Object.keys((await readPackageData()).scripts ?? {});

if (
scripts.reduce(
(acc, curr) => (everythingScripts.has(curr) ? acc + 1 : acc),
0,
) >= 3
) {
return "everything";
}

if (
scripts.reduce(
(acc, curr) => (commonScripts.has(curr) ? acc + 1 : acc),
0,
) >= 2
) {
return "common";
}

return "minimum";
}
43 changes: 43 additions & 0 deletions src/shared/options/readOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ vi.mock("./createOptionDefaults/index.js", () => ({
},
}));

const mockReadPackageData = vi.fn();
vi.mock("../packages.js", () => ({
get readPackageData() {
return mockReadPackageData;
},
}));

describe("readOptions", () => {
it("returns a cancellation when an arg is invalid", async () => {
const validationResult = z
Expand Down Expand Up @@ -524,4 +531,40 @@ describe("readOptions", () => {
},
});
});

it("infers base from package scripts during migration", async () => {
mockReadPackageData.mockImplementationOnce(() =>
Promise.resolve({
scripts: {
build: "build",
lint: "lint",
test: "test",
},
}),
);
expect(await readOptions(["--offline"], "migrate")).toStrictEqual({
cancelled: false,
github: mockOptions.github,
options: {
...emptyOptions,
...mockOptions,
access: "public",
base: "minimum",
description: "mock",
directory: "mock",
email: {
github: "mock",
npm: "mock",
},
guide: undefined,
logo: undefined,
mode: "migrate",
offline: true,
owner: "mock",
skipAllContributorsApi: true,
skipGitHubApi: true,
title: "mock",
},
});
});
});
5 changes: 5 additions & 0 deletions src/shared/options/readOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { augmentOptionsWithExcludes } from "./augmentOptionsWithExcludes.js";
import { createOptionDefaults } from "./createOptionDefaults/index.js";
import { detectEmailRedundancy } from "./detectEmailRedundancy.js";
import { ensureRepositoryExists } from "./ensureRepositoryExists.js";
import { getBase } from "./getBase.js";
import { GitHub, getGitHub } from "./getGitHub.js";
import { getPrefillOrPromptedOption } from "./getPrefillOrPromptedOption.js";
import { optionsSchema } from "./optionsSchema.js";
Expand Down Expand Up @@ -44,6 +45,10 @@ export async function readOptions(
tokens: true,
});

if (mode === "migrate" && !values.base) {
values.base = await getBase();
}

const mappedOptions = {
access: values.access,
author: values.author,
Expand Down
1 change: 1 addition & 0 deletions src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface PartialPackageData {
email?: string;
name?: string;
repository?: { type: string; url: string } | string;
scripts?: Record<string, string>;
}

export type OptionsAccess = "public" | "restricted";
Expand Down

0 comments on commit 20afaf4

Please sign in to comment.