@ingram-tech/nk-dev 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -6
- package/bin/nk.js +7 -2
- package/guide.md +1 -1
- package/lib/init.js +29 -14
- package/lib/knip.js +36 -0
- package/lib/passthrough.js +12 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
The nextkit **dev toolchain in one package**. Everything a site needs at
|
|
4
4
|
development time — and nothing that ships to production — lives here:
|
|
5
5
|
|
|
6
|
-
- the **`nk` CLI** (`nk dev` / `format` / `lint` / `check` / `type-check` / `build`);
|
|
6
|
+
- the **`nk` CLI** (`nk dev` / `format` / `lint` / `knip` / `check` / `type-check` / `build`);
|
|
7
7
|
- the shared **oxlint + oxfmt**, **TypeScript**, and **Vitest** config;
|
|
8
|
+
- **knip** (unused dependency / export / file detection), bundled and run by `nk check`;
|
|
8
9
|
- the **oxfmt format-on-commit** git hook (`nextkit-format-staged`);
|
|
9
10
|
- the **AI agent guide** (`guide.md`) imported into a site's `CLAUDE.md`;
|
|
10
11
|
- **`nk init`**, which scaffolds a site to use all of the above.
|
|
11
12
|
|
|
12
13
|
It's a single `devDependency` and pulls the toolchain (oxlint, oxfmt, tsc,
|
|
13
|
-
vitest, jsdom, jest-dom) as hard dependencies — so one install gives you
|
|
14
|
-
whole stack instead of re-listing each tool per site.
|
|
14
|
+
vitest, jsdom, jest-dom, knip) as hard dependencies — so one install gives you
|
|
15
|
+
the whole stack instead of re-listing each tool per site.
|
|
15
16
|
|
|
16
17
|
> **Runtime vs dev-time.** nk-dev is the *dev-time* bundle. Runtime features
|
|
17
18
|
> (`@ingram-tech/email`, `nk-db`, `nk-auth`, …) stay separate packages that
|
|
@@ -33,10 +34,14 @@ bun install # the prepare script wires the git hook
|
|
|
33
34
|
| `.oxlintrc.json` | `extends` the shared oxlint rules (relative path — oxlint doesn't resolve package specifiers) |
|
|
34
35
|
| `.oxfmtrc.json` | a copy of the house format config (oxfmt has no `extends`) |
|
|
35
36
|
| `tsconfig.json` | `extends` `@ingram-tech/nk-dev/tsconfig/nextjs.json` + the site's own `include`/`paths` |
|
|
36
|
-
| `
|
|
37
|
+
| `knip.json` | seed config ignoring `@ingram-tech/nk-dev` (knip has no shareable config) |
|
|
37
38
|
| `.githooks/pre-commit` + `prepare` script | oxfmt format-on-commit |
|
|
38
39
|
| `CLAUDE.md` | the agent-guide `@import` |
|
|
39
40
|
|
|
41
|
+
It also prints a `vitest.config.ts` snippet (`mergeConfig(nextkitTestConfig, {})`
|
|
42
|
+
from `@ingram-tech/nk-dev/vitest`) rather than writing the file — add it only if
|
|
43
|
+
you test with Vitest (many sites use `bun:test`).
|
|
44
|
+
|
|
40
45
|
Everything is `extends`-based, so the house config is enforced by default but
|
|
41
46
|
overridable — layer your own rules on top, or replace a stub entirely (e.g. drop
|
|
42
47
|
in a `biome.json` instead of the oxlint/oxfmt stubs).
|
|
@@ -85,8 +90,10 @@ tsc), so versions stay under each site's control — nk just orchestrates.
|
|
|
85
90
|
- **`nk format` / `nk format --check`** — formats code (JS/TS/JSON/CSS) with
|
|
86
91
|
oxfmt and SQL with Prettier. `--check` verifies without writing (CI).
|
|
87
92
|
- **`nk lint`** — `oxlint`.
|
|
88
|
-
- **`nk
|
|
89
|
-
|
|
93
|
+
- **`nk knip`** — `knip` (unused dependencies / exports / files).
|
|
94
|
+
- **`nk check`** — `oxlint` + `oxfmt --check` + SQL format verification + `knip`
|
|
95
|
+
(only when the repo has a knip config) + the agent-guide import gate. The CI
|
|
96
|
+
gate; runs every checker and reports them all before failing.
|
|
90
97
|
- **`nk type-check`** — `next typegen && tsc --noEmit`.
|
|
91
98
|
- **`nk build [...]`** — `next build`, extra args passed through.
|
|
92
99
|
|
package/bin/nk.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { dev } from "../lib/dev.js";
|
|
3
3
|
import { format } from "../lib/format.js";
|
|
4
4
|
import { init } from "../lib/init.js";
|
|
5
|
+
import { knip } from "../lib/knip.js";
|
|
5
6
|
import { build, check, lint, typeCheck } from "../lib/passthrough.js";
|
|
6
7
|
|
|
7
8
|
const USAGE = `nk — the nextkit CLI
|
|
@@ -17,8 +18,9 @@ Commands:
|
|
|
17
18
|
format [--check] Format code with oxfmt and SQL with Prettier. --check
|
|
18
19
|
verifies without writing (for CI).
|
|
19
20
|
lint Lint with oxlint.
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
knip Find unused dependencies / exports / files with knip.
|
|
22
|
+
check The CI gate: lint + format verify + SQL + knip (when
|
|
23
|
+
configured) + the agent-guide import gate.
|
|
22
24
|
type-check next typegen && tsc --noEmit.
|
|
23
25
|
build [...] next build (extra args passed through).
|
|
24
26
|
|
|
@@ -39,6 +41,9 @@ switch (cmd) {
|
|
|
39
41
|
case "lint":
|
|
40
42
|
lint();
|
|
41
43
|
break;
|
|
44
|
+
case "knip":
|
|
45
|
+
knip(rest);
|
|
46
|
+
break;
|
|
42
47
|
case "check":
|
|
43
48
|
await check();
|
|
44
49
|
break;
|
package/guide.md
CHANGED
|
@@ -55,6 +55,6 @@ the UI/page tree, and never expose internal plumbing under `/api/`.
|
|
|
55
55
|
- `@ingram-tech/nk-db` — Postgres data layer: `createPool` (one TLS-aware pool) + `createQueries` (raw SQL) + `createDb` (Drizzle), plus a PGlite dev/test harness at `@ingram-tech/nk-db/pglite`
|
|
56
56
|
- `@ingram-tech/bot-protection` — invisible form protection (honeypot + timing + Vercel BotID)
|
|
57
57
|
- `@ingram-tech/newsletter` — Supabase newsletter: subscribe / send, 1-click unsubscribe
|
|
58
|
-
- `@ingram-tech/nk-dev` — the whole dev toolchain in one devDependency: the `nk` command (`nk dev` boots local PGlite via `@ingram-tech/nk-db` if installed, then Next; plus `nk format` / `lint` / `check` / `type-check` / `build`), the shared oxlint + oxfmt / TypeScript / Vitest config, the oxfmt format-on-commit hook, and this guide. `nk init` scaffolds a site to use it all.
|
|
58
|
+
- `@ingram-tech/nk-dev` — the whole dev toolchain in one devDependency: the `nk` command (`nk dev` boots local PGlite via `@ingram-tech/nk-db` if installed, then Next; plus `nk format` / `lint` / `knip` / `check` / `type-check` / `build`), the shared oxlint + oxfmt / TypeScript / Vitest config, knip, the oxfmt format-on-commit hook, and this guide. `nk check` runs every fast checker (oxlint, oxfmt, SQL, knip) in one gate. `nk init` scaffolds a site to use it all.
|
|
59
59
|
|
|
60
60
|
For detail on any package, read its README in `node_modules/@ingram-tech/<pkg>/`.
|
package/lib/init.js
CHANGED
|
@@ -48,13 +48,17 @@ const TSCONFIG = {
|
|
|
48
48
|
exclude: ["node_modules"],
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
const VITEST_HINT = `import { mergeConfig } from "vitest/config";\\nimport { nextkitTestConfig } from "@ingram-tech/nk-dev/vitest";\\nexport default mergeConfig(nextkitTestConfig, {});`;
|
|
52
|
+
|
|
53
|
+
// knip has no shareable config, so each site carries its own. This seed ignores
|
|
54
|
+
// @ingram-tech/nk-dev: a site that runs raw tools (not the `nk` bin) gives knip
|
|
55
|
+
// no way to see nk-dev as used, so without this it'd fail as an unused
|
|
56
|
+
// dependency. (Sites that do call `nk` in scripts can drop it — knip will hint.)
|
|
57
|
+
// Add `entry`/`ignore` as the project grows.
|
|
58
|
+
const KNIP = {
|
|
59
|
+
$schema: "https://unpkg.com/knip@6/schema.json",
|
|
60
|
+
ignoreDependencies: ["@ingram-tech/nk-dev"],
|
|
61
|
+
};
|
|
58
62
|
|
|
59
63
|
const PRE_COMMIT = `#!/bin/sh
|
|
60
64
|
# nextkit pre-commit: format staged files with oxfmt, then re-stage them.
|
|
@@ -85,18 +89,20 @@ export function init() {
|
|
|
85
89
|
// 3. TypeScript config.
|
|
86
90
|
writeIfAbsent(resolve(cwd, "tsconfig.json"), (f) => writeJson(f, TSCONFIG));
|
|
87
91
|
|
|
88
|
-
// 4. Vitest config
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
// 4. Vitest config — only hint, never auto-write. Many sites test with
|
|
93
|
+
// `bun:test` rather than Vitest, and an unused vitest.config.ts is noise.
|
|
94
|
+
hintVitestConfig(cwd);
|
|
95
|
+
|
|
96
|
+
// 5. knip config (unused deps/exports/files; run by `nk check`).
|
|
97
|
+
writeIfAbsent(resolve(cwd, "knip.json"), (f) => writeJson(f, KNIP));
|
|
92
98
|
|
|
93
|
-
//
|
|
99
|
+
// 6. Format-on-commit hook + git wiring.
|
|
94
100
|
setupGitHook(cwd);
|
|
95
101
|
|
|
96
|
-
//
|
|
102
|
+
// 7. Make sure the agent guide is imported into CLAUDE.md.
|
|
97
103
|
ensureGuideImport(cwd);
|
|
98
104
|
|
|
99
|
-
//
|
|
105
|
+
// 8. A `prepare` script so the hook re-wires itself on every `bun install`.
|
|
100
106
|
ensurePrepareScript(cwd);
|
|
101
107
|
|
|
102
108
|
log("done. Next: `bun install`, then `nk check`.");
|
|
@@ -137,6 +143,15 @@ function ensureGuideImport(cwd) {
|
|
|
137
143
|
log("added the agent-guide import to CLAUDE.md");
|
|
138
144
|
}
|
|
139
145
|
|
|
146
|
+
function hintVitestConfig(cwd) {
|
|
147
|
+
if (existsSync(resolve(cwd, "vitest.config.ts"))) {
|
|
148
|
+
skip("vitest.config.ts");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
log("no vitest.config.ts — if you test with Vitest, create one:");
|
|
152
|
+
console.log(` ${VITEST_HINT.replace(/\\n/g, "\n ")}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
140
155
|
function ensurePrepareScript(cwd) {
|
|
141
156
|
const pkgPath = resolve(cwd, "package.json");
|
|
142
157
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
package/lib/knip.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { run } from "./run.js";
|
|
4
|
+
|
|
5
|
+
// Knip has no shareable/extends config, so each repo carries its own. We treat
|
|
6
|
+
// the *presence* of a knip config as opt-in: `nk check` only runs knip when one
|
|
7
|
+
// of these exists, so sites that haven't adopted knip aren't suddenly gated.
|
|
8
|
+
const CONFIG_FILES = [
|
|
9
|
+
"knip.json",
|
|
10
|
+
"knip.jsonc",
|
|
11
|
+
"knip.config.js",
|
|
12
|
+
"knip.config.ts",
|
|
13
|
+
"knip.ts",
|
|
14
|
+
"knip.js",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/** Whether the project has a knip config (a file, or a package.json#knip key). */
|
|
18
|
+
export function hasKnipConfig(cwd = process.cwd()) {
|
|
19
|
+
if (CONFIG_FILES.some((f) => existsSync(resolve(cwd, f)))) return true;
|
|
20
|
+
try {
|
|
21
|
+
const pkg = JSON.parse(readFileSync(resolve(cwd, "package.json"), "utf8"));
|
|
22
|
+
return Boolean(pkg.knip);
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Run knip (dependency/export/file hygiene). Returns its exit code. */
|
|
29
|
+
export function runKnip(extraArgs = []) {
|
|
30
|
+
return run("knip", extraArgs);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** `nk knip` — run knip directly. */
|
|
34
|
+
export function knip(extraArgs = []) {
|
|
35
|
+
process.exit(runKnip(extraArgs));
|
|
36
|
+
}
|
package/lib/passthrough.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { checkAgentGuideImport } from "./agent-guide.js";
|
|
2
2
|
import { formatSql } from "./format.js";
|
|
3
3
|
import { FORMATTER } from "./formatter.js";
|
|
4
|
+
import { hasKnipConfig, runKnip } from "./knip.js";
|
|
4
5
|
import { run } from "./run.js";
|
|
5
6
|
|
|
6
7
|
/** `nk lint` — oxlint. */
|
|
@@ -8,7 +9,11 @@ export function lint() {
|
|
|
8
9
|
process.exit(run(FORMATTER.lint[0], FORMATTER.lint[1]));
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* `nk check` — the CI gate. Runs every fast checker and reports them all before
|
|
14
|
+
* failing: oxlint, oxfmt (format), SQL format, knip (when configured), and the
|
|
15
|
+
* agent-guide import gate.
|
|
16
|
+
*/
|
|
12
17
|
export async function check() {
|
|
13
18
|
// Run every gate before deciding (no short-circuit), so one failure doesn't
|
|
14
19
|
// hide another. oxc splits lint (oxlint) and format (oxfmt), so we run both.
|
|
@@ -16,6 +21,9 @@ export async function check() {
|
|
|
16
21
|
const fmtFailed = run(FORMATTER.checkFormat[0], FORMATTER.checkFormat[1]) !== 0;
|
|
17
22
|
await formatSql({ check: true });
|
|
18
23
|
const sqlFailed = Boolean(process.exitCode);
|
|
24
|
+
// knip (unused deps/exports/files). Opt-in: only when the repo has a knip
|
|
25
|
+
// config — knip has no shareable config, so absence means "not adopted".
|
|
26
|
+
const knipFailed = hasKnipConfig() ? runKnip() !== 0 : false;
|
|
19
27
|
// Keep the site on the shared-guidance channel: if it depends on
|
|
20
28
|
// @ingram-tech/nk-dev, its CLAUDE.md must @import the guide.
|
|
21
29
|
const guide = checkAgentGuideImport();
|
|
@@ -25,7 +33,9 @@ export async function check() {
|
|
|
25
33
|
" → add `@./node_modules/@ingram-tech/nk-dev/guide.md` to your CLAUDE.md (or run `nk init`).",
|
|
26
34
|
);
|
|
27
35
|
}
|
|
28
|
-
process.exit(
|
|
36
|
+
process.exit(
|
|
37
|
+
lintFailed || fmtFailed || sqlFailed || knipFailed || !guide.ok ? 1 : 0,
|
|
38
|
+
);
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
/** `nk type-check` — the house type-check: regenerate Next's types, then tsc. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingram-tech/nk-dev",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The nextkit dev toolchain in one package: the `nk` CLI plus shared oxlint/oxfmt, TypeScript, and Vitest config, the format-on-commit hook, and the AI agent guide. `nk init` scaffolds a site to use it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@testing-library/jest-dom": "^6.9.1",
|
|
47
47
|
"jsdom": "^29.1.1",
|
|
48
|
+
"knip": "^6.17.1",
|
|
48
49
|
"oxfmt": "^0.55.0",
|
|
49
50
|
"oxlint": "^1.70.0",
|
|
50
51
|
"prettier": "^3.8.3",
|