Skip to content

Commit

Permalink
fix: use Knip version from installed dependency (#1792)
Browse files Browse the repository at this point in the history
## PR Checklist

- [x] Addresses an existing open issue: fixes #1638
- [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

Uses the same `readFileSafeAsJson` strategy as `writePackageJson`.

💖
  • Loading branch information
JoshuaKGoldberg authored Dec 21, 2024
1 parent d789679 commit b39d65f
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 33 deletions.
2 changes: 1 addition & 1 deletion knip.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://unpkg.com/knip@latest/schema.json",
"$schema": "https://unpkg.com/knip@5.41.0/schema.json",
"entry": ["script/*e2e.js", "src/index.ts!", "src/**/*.test.*"],
"ignoreDependencies": ["all-contributors-cli"],
"ignoreExportsUsedInFile": { "interface": true, "type": true },
Expand Down
2 changes: 1 addition & 1 deletion script/__snapshots__/migrate-test-e2e.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ exports[`expected file changes > knip.json 1`] = `
+++ b/knip.json
@@ ... @@
{
"$schema": "https://unpkg.com/knip@latest/schema.json",
"$schema": "https://unpkg.com/knip@5.41.0/schema.json",
- "entry": ["script/*e2e.js", "src/index.ts!", "src/**/*.test.*"],
- "ignoreDependencies": ["all-contributors-cli"],
+ "entry": ["src/index.ts!"],
Expand Down
4 changes: 2 additions & 2 deletions src/next/blocks/blockKnip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { base } from "../base.js";
import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js";
import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js";
import { blockPackageJson } from "./blockPackageJson.js";
import { getPackageDependencies } from "./packageData.js";
import { getPackageDependencies, getPackageDependency } from "./packageData.js";

export const blockKnip = base.createBlock({
about: {
Expand Down Expand Up @@ -41,7 +41,7 @@ export const blockKnip = base.createBlock({
],
files: {
"knip.json": JSON.stringify({
$schema: "https://unpkg.com/knip@latest/schema.json",
$schema: `https://unpkg.com/knip@${getPackageDependency("knip")}/schema.json`,
entry: ["src/index.ts!"],
ignoreExportsUsedInFile: {
interface: true,
Expand Down
45 changes: 25 additions & 20 deletions src/next/blocks/packageData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,33 @@ const packageData =
// Importing from above src/ would expand the TS build rootDir
require("../../../package.json") as typeof import("../../../package.json");

const getPackageInner = (
export function getPackageDependencies(...names: string[]) {
return Object.fromEntries(
names.map((name) => {
return [name, getPackageDependency(name)];
}),
);
}

export function getPackageDependency(name: string) {
const version =
getPackageInner("devDependencies", name) ??
getPackageInner("dependencies", name);

if (!version) {
throw new Error(
`'${name} is neither in package.json's dependencies nor its devDependencies.`,
);
}

return version;
}

function getPackageInner(
key: "dependencies" | "devDependencies",
name: string,
) => {
) {
const inner = packageData[key];

return inner[name as keyof typeof inner] as string | undefined;
};

export const getPackageDependencies = (...names: string[]) =>
Object.fromEntries(
names.map((name) => {
const version =
getPackageInner("devDependencies", name) ??
getPackageInner("dependencies", name);

if (!version) {
throw new Error(
`'${name} is neither in package.json's dependencies nor its devDependencies.`,
);
}

return [name, version];
}),
);
}
66 changes: 66 additions & 0 deletions src/steps/writing/creation/createKnipConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, it, vi } from "vitest";

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

const mockReadFileSafeAsJson = vi.fn();

vi.mock("../../../shared/readFileSafeAsJson.js", () => ({
get readFileSafeAsJson() {
return mockReadFileSafeAsJson;
},
}));

describe("createKnipConfig", () => {
it("uses uses the knip version from package.json devDependencies when it exists", async () => {
const version = "1.2.3";
mockReadFileSafeAsJson.mockResolvedValueOnce({
devDependencies: { knip: version },
});

const packageJson = await createKnipConfig();

expect(JSON.parse(packageJson)).toEqual({
$schema: `https://unpkg.com/knip@${version}/schema.json`,
entry: ["src/index.ts!"],
ignoreExportsUsedInFile: {
interface: true,
type: true,
},
project: ["src/**/*.ts!"],
});
});

it("uses version 'latest' when the package.json does not exist", async () => {
mockReadFileSafeAsJson.mockResolvedValueOnce(undefined);

const packageJson = await createKnipConfig();

expect(JSON.parse(packageJson)).toEqual({
$schema: `https://unpkg.com/knip@latest/schema.json`,
entry: ["src/index.ts!"],
ignoreExportsUsedInFile: {
interface: true,
type: true,
},
project: ["src/**/*.ts!"],
});
});

it("uses version 'latest' when the package.json exists but does not have knip in devDependencies", async () => {
mockReadFileSafeAsJson.mockResolvedValueOnce({
dependencies: {},
});

const packageJson = await createKnipConfig();

expect(JSON.parse(packageJson)).toEqual({
$schema: `https://unpkg.com/knip@latest/schema.json`,
entry: ["src/index.ts!"],
ignoreExportsUsedInFile: {
interface: true,
type: true,
},
project: ["src/**/*.ts!"],
});
});
});
19 changes: 19 additions & 0 deletions src/steps/writing/creation/createKnipConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { readFileSafeAsJson } from "../../../shared/readFileSafeAsJson.js";
import { PartialPackageData } from "../../../shared/types.js";
import { formatJson } from "./formatters/formatJson.js";

export async function createKnipConfig() {
const existingPackageJson = (await readFileSafeAsJson(
"./package.json",
)) as null | PartialPackageData;

return await formatJson({
$schema: `https://unpkg.com/knip@${existingPackageJson?.devDependencies?.knip ?? "latest"}/schema.json`,
entry: ["src/index.ts!"],
ignoreExportsUsedInFile: {
interface: true,
type: true,
},
project: ["src/**/*.ts!"],
});
}
11 changes: 2 additions & 9 deletions src/steps/writing/creation/rootFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Options } from "../../../shared/types.js";
import { createCSpellConfig } from "./createCSpellConfig.js";
import { createDotGitignore } from "./createDotGitignore.js";
import { createESLintConfig } from "./createESLintConfig.js";
import { createKnipConfig } from "./createKnipConfig.js";
import { createTsupConfig } from "./createTsupConfig.js";
import { formatIgnoreFile } from "./formatters/formatIgnoreFile.js";
import { formatJson } from "./formatters/formatJson.js";
Expand Down Expand Up @@ -96,15 +97,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"cspell.json": await createCSpellConfig(options),
}),
...(!options.excludeLintKnip && {
"knip.json": await formatJson({
$schema: "https://unpkg.com/knip@latest/schema.json",
entry: ["src/index.ts!"],
ignoreExportsUsedInFile: {
interface: true,
type: true,
},
project: ["src/**/*.ts!"],
}),
"knip.json": await createKnipConfig(),
}),
"package.json": await writePackageJson(options),
"tsconfig.json": await formatJson({
Expand Down

0 comments on commit b39d65f

Please sign in to comment.