bun-doctor 0.0.1 → 0.0.3
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 +36 -27
- package/bin/bun-doctor.js +0 -0
- package/dist/cli.mjs +38 -13
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +8 -3
- package/dist/index.mjs +1 -1
- package/dist/scan-BtfKcvQl.mjs +1108 -0
- package/dist/scan-BtfKcvQl.mjs.map +1 -0
- package/docs/compat-db.md +34 -10
- package/docs/eval-harness.md +89 -0
- package/docs/rule-spec.md +8 -9
- package/package.json +7 -2
- package/dist/scan-BVcJTreL.mjs +0 -749
- package/dist/scan-BVcJTreL.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
# Bun Doctor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://npmjs.com/package/bun-doctor)
|
|
4
|
+
[](https://npmjs.com/package/bun-doctor)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
> Can this repo safely move to Bun, and what exact changes get it there?
|
|
8
|
-
|
|
9
|
-
It scans package manager state, lockfiles, Bun config, TypeScript config, CI workflows, dependency risk, and a first pass of Bun-specific code risks. The output is a 0-100 Bun Readiness score grouped into Blockers, Risks, Migration work, and Bun wins.
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
6
|
+
Scan a Node.js project to see how ready it is to move to Bun.
|
|
12
7
|
|
|
13
8
|
```bash
|
|
14
9
|
npx -y bun-doctor@latest .
|
|
15
10
|
```
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
bun install
|
|
21
|
-
bun run build
|
|
22
|
-
node dist/cli.mjs . --verbose
|
|
23
|
-
```
|
|
12
|
+
You get a 0-100 Bun Readiness score and a grouped list of Blockers, Risks, Migration work, and Bun wins. The scanner inspects package manager state, lockfiles, `bunfig.toml`, `tsconfig.json`, GitHub Actions workflows, dependency compatibility, and Bun-specific code risks.
|
|
24
13
|
|
|
25
14
|
## CLI
|
|
26
15
|
|
|
@@ -43,21 +32,41 @@ Commands:
|
|
|
43
32
|
|
|
44
33
|
## Scoring
|
|
45
34
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
35
|
+
Each unique rule triggered subtracts from a starting score of 100:
|
|
36
|
+
|
|
37
|
+
| Level | Penalty |
|
|
38
|
+
| --- | ---: |
|
|
39
|
+
| Blocker | 12 |
|
|
40
|
+
| Risk | 5 |
|
|
41
|
+
| Migration | 2 |
|
|
42
|
+
| Win | 0 |
|
|
43
|
+
|
|
44
|
+
Wins are surfaced as optional Bun-native simplifications and never lower the score.
|
|
45
|
+
|
|
46
|
+
## GitHub Action
|
|
47
|
+
|
|
48
|
+
Drop this into `.github/workflows/bun-doctor.yml`:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
name: Bun Doctor
|
|
52
|
+
on:
|
|
53
|
+
pull_request:
|
|
54
|
+
push:
|
|
55
|
+
branches: [main]
|
|
56
|
+
jobs:
|
|
57
|
+
bun-doctor:
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v5
|
|
61
|
+
- uses: kylegrahammatzen/bun-doctor@main
|
|
62
|
+
```
|
|
54
63
|
|
|
55
|
-
##
|
|
64
|
+
## Sources
|
|
56
65
|
|
|
57
|
-
Every diagnostic
|
|
66
|
+
Every diagnostic and compatibility entry carries at least one verifiable source: Bun documentation, the Node compatibility table, or an issue/test link. No source, no rule.
|
|
58
67
|
|
|
59
68
|
## Roadmap
|
|
60
69
|
|
|
61
|
-
- MVP: CLI, JSON, score, config
|
|
62
|
-
- v1.0: public eval harness that verifies compatibility
|
|
70
|
+
- MVP: CLI, JSON, score, package/config rules, code-risk scans, compatibility DB v0.
|
|
71
|
+
- v1.0: public eval harness that verifies compatibility entries across Bun versions and platforms ([docs/eval-harness.md](docs/eval-harness.md)).
|
|
63
72
|
- Later: editor/linter plugin once rules prove useful in real migrations.
|
package/bin/bun-doctor.js
CHANGED
|
File without changes
|
package/dist/cli.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as VERSION, i as toRelativePath, t as scan } from "./scan-
|
|
2
|
+
import { a as VERSION, i as toRelativePath, t as scan } from "./scan-BtfKcvQl.mjs";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import fs from "node:fs";
|
|
6
|
+
import pc from "picocolors";
|
|
6
7
|
//#region src/install-skill.ts
|
|
7
8
|
const SKILL_CONTENT = `---
|
|
8
9
|
name: bun-doctor
|
|
@@ -36,6 +37,18 @@ const LEVEL_SYMBOL = {
|
|
|
36
37
|
migration: "~",
|
|
37
38
|
win: "+"
|
|
38
39
|
};
|
|
40
|
+
const LEVEL_COLOR = {
|
|
41
|
+
blocker: pc.red,
|
|
42
|
+
risk: pc.yellow,
|
|
43
|
+
migration: pc.cyan,
|
|
44
|
+
win: pc.green
|
|
45
|
+
};
|
|
46
|
+
const SCORE_COLOR = {
|
|
47
|
+
Ready: pc.green,
|
|
48
|
+
Close: pc.yellow,
|
|
49
|
+
Risky: pc.magenta,
|
|
50
|
+
Blocked: pc.red
|
|
51
|
+
};
|
|
39
52
|
const groupByCategory = (diagnostics) => {
|
|
40
53
|
const groups = /* @__PURE__ */ new Map();
|
|
41
54
|
for (const diagnostic of diagnostics) {
|
|
@@ -47,33 +60,45 @@ const groupByCategory = (diagnostics) => {
|
|
|
47
60
|
};
|
|
48
61
|
const formatLocation = (diagnostic, rootDirectory) => {
|
|
49
62
|
const relativePath = toRelativePath(path.resolve(diagnostic.filePath), rootDirectory);
|
|
50
|
-
return diagnostic.line >
|
|
63
|
+
return diagnostic.line > 1 ? `${relativePath}:${diagnostic.line}` : relativePath;
|
|
51
64
|
};
|
|
52
65
|
const formatTextReport = (result, verbose) => {
|
|
53
66
|
const lines = [];
|
|
54
|
-
|
|
55
|
-
lines.push(
|
|
56
|
-
lines.push(
|
|
57
|
-
lines.push(
|
|
67
|
+
const scoreColor = SCORE_COLOR[result.score.label];
|
|
68
|
+
lines.push(pc.bold("bun-doctor"));
|
|
69
|
+
lines.push(`${pc.dim("Project:")} ${result.project.packageName}`);
|
|
70
|
+
lines.push(`${pc.dim("Bun Readiness:")} ${scoreColor(pc.bold(`${result.score.score}/100`))} ${pc.dim(`(${result.score.label})`)}`);
|
|
71
|
+
lines.push(`${pc.dim("Findings:")} ${pc.red(`${result.summary.blockers} blockers`)}, ${pc.yellow(`${result.summary.risks} risks`)}, ${pc.cyan(`${result.summary.migrations} migration`)}, ${pc.green(`${result.summary.wins} wins`)}`);
|
|
58
72
|
lines.push("");
|
|
59
73
|
if (result.diagnostics.length === 0) {
|
|
60
|
-
lines.push("No Bun migration findings.");
|
|
74
|
+
lines.push(pc.green("No Bun migration findings."));
|
|
61
75
|
return lines.join("\n");
|
|
62
76
|
}
|
|
63
77
|
const groups = groupByCategory(result.diagnostics);
|
|
64
78
|
for (const category of CATEGORY_ORDER) {
|
|
65
79
|
const diagnostics = groups.get(category) ?? [];
|
|
66
80
|
if (diagnostics.length === 0) continue;
|
|
67
|
-
|
|
81
|
+
const firstLevel = diagnostics[0]?.level;
|
|
82
|
+
const categoryColor = firstLevel ? LEVEL_COLOR[firstLevel] : pc.white;
|
|
83
|
+
lines.push(pc.bold(categoryColor(`${category} (${diagnostics.length})`)));
|
|
68
84
|
const shownDiagnostics = verbose ? diagnostics : diagnostics.slice(0, 3);
|
|
69
85
|
for (const diagnostic of shownDiagnostics) {
|
|
70
|
-
|
|
86
|
+
const symbol = LEVEL_COLOR[diagnostic.level](LEVEL_SYMBOL[diagnostic.level]);
|
|
87
|
+
lines.push(` ${symbol} ${pc.bold(diagnostic.title)} ${pc.dim(`[${diagnostic.ruleId}]`)}`);
|
|
71
88
|
lines.push(` ${diagnostic.message}`);
|
|
72
|
-
if (diagnostic.
|
|
73
|
-
lines.push(` ${
|
|
74
|
-
lines.push(`
|
|
89
|
+
if (diagnostic.replacement) lines.push(` ${pc.green(`→ ${diagnostic.replacement}`)}`);
|
|
90
|
+
if (diagnostic.help) lines.push(` ${pc.dim(diagnostic.help)}`);
|
|
91
|
+
lines.push(` ${pc.cyan(formatLocation(diagnostic, result.project.rootDirectory))}`);
|
|
92
|
+
if (diagnostic.alsoIn && diagnostic.alsoIn.length > 0) {
|
|
93
|
+
const aggregated = diagnostic.alsoIn.map((alsoPath) => toRelativePath(path.resolve(alsoPath), result.project.rootDirectory)).join(", ");
|
|
94
|
+
lines.push(` ${pc.dim(`Also in: ${aggregated}`)}`);
|
|
95
|
+
}
|
|
96
|
+
lines.push(` ${pc.dim(`Source: ${diagnostic.sources[0]}`)}`);
|
|
97
|
+
}
|
|
98
|
+
if (!verbose && diagnostics.length > shownDiagnostics.length) {
|
|
99
|
+
const hidden = diagnostics.length - shownDiagnostics.length;
|
|
100
|
+
lines.push(` ${pc.dim(`... ${hidden} more — re-run with --verbose`)}`);
|
|
75
101
|
}
|
|
76
|
-
if (!verbose && diagnostics.length > shownDiagnostics.length) lines.push(` ... ${diagnostics.length - shownDiagnostics.length} more. Re-run with --verbose.`);
|
|
77
102
|
lines.push("");
|
|
78
103
|
}
|
|
79
104
|
return lines.join("\n").trimEnd();
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/install-skill.ts","../src/report.ts","../src/cli.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\ninterface InstallSkillOptions {\n directory: string;\n dryRun?: boolean;\n}\n\nconst SKILL_CONTENT = `---\nname: bun-doctor\ndescription: Use after making dependency, CI, package manager, test runner, or Node runtime changes in a project that uses or is migrating to Bun. Checks Bun readiness and migration risk.\nversion: \"1.0.0\"\n---\n\n# Bun Doctor\n\nRun \\`npx -y bun-doctor@latest . --verbose\\` after Bun migration changes and fix blockers before switching CI or runtime.\n`;\n\nexport const installSkill = (options: InstallSkillOptions): string => {\n const skillDirectory = path.join(options.directory, \".agents\", \"skills\", \"bun-doctor\");\n const skillPath = path.join(skillDirectory, \"SKILL.md\");\n\n if (options.dryRun) return skillPath;\n\n fs.mkdirSync(skillDirectory, { recursive: true });\n fs.writeFileSync(skillPath, SKILL_CONTENT, \"utf8\");\n return skillPath;\n};\n","import path from \"node:path\";\nimport type { Diagnostic, FindingCategory, ScanResult } from \"./types.js\";\nimport { toRelativePath } from \"./utils.js\";\n\nconst CATEGORY_ORDER: FindingCategory[] = [\"Blockers\", \"Risks\", \"Migration work\", \"Bun wins\"];\n\nconst LEVEL_SYMBOL: Record<Diagnostic[\"level\"], string> = {\n blocker: \"x\",\n risk: \"!\",\n migration: \"~\",\n win: \"+\",\n};\n\nconst groupByCategory = (diagnostics: Diagnostic[]): Map<FindingCategory, Diagnostic[]> => {\n const groups = new Map<FindingCategory, Diagnostic[]>();\n for (const diagnostic of diagnostics) {\n const existing = groups.get(diagnostic.category) ?? [];\n existing.push(diagnostic);\n groups.set(diagnostic.category, existing);\n }\n return groups;\n};\n\nconst formatLocation = (diagnostic: Diagnostic, rootDirectory: string): string => {\n const relativePath = toRelativePath(path.resolve(diagnostic.filePath), rootDirectory);\n return diagnostic.line > 0 ? `${relativePath}:${diagnostic.line}` : relativePath;\n};\n\nexport const formatTextReport = (result: ScanResult, verbose: boolean): string => {\n const lines: string[] = [];\n lines.push(`bun-doctor`);\n lines.push(`Project: ${result.project.packageName}`);\n lines.push(`Bun Readiness: ${result.score.score}/100 (${result.score.label})`);\n lines.push(\n `Findings: ${result.summary.blockers} blockers, ${result.summary.risks} risks, ${result.summary.migrations} migration, ${result.summary.wins} wins`,\n );\n lines.push(\"\");\n\n if (result.diagnostics.length === 0) {\n lines.push(\"No Bun migration findings.\");\n return lines.join(\"\\n\");\n }\n\n const groups = groupByCategory(result.diagnostics);\n for (const category of CATEGORY_ORDER) {\n const diagnostics = groups.get(category) ?? [];\n if (diagnostics.length === 0) continue;\n lines.push(`${category} (${diagnostics.length})`);\n const shownDiagnostics = verbose ? diagnostics : diagnostics.slice(0, 3);\n for (const diagnostic of shownDiagnostics) {\n lines.push(` ${LEVEL_SYMBOL[diagnostic.level]} ${diagnostic.title} [${diagnostic.ruleId}]`);\n lines.push(` ${diagnostic.message}`);\n if (diagnostic.help) lines.push(` ${diagnostic.help}`);\n lines.push(` ${formatLocation(diagnostic, result.project.rootDirectory)}`);\n lines.push(` Source: ${diagnostic.sources[0]}`);\n }\n if (!verbose && diagnostics.length > shownDiagnostics.length) {\n lines.push(` ... ${diagnostics.length - shownDiagnostics.length} more. Re-run with --verbose.`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\").trimEnd();\n};\n\nexport const toJsonReport = (result: ScanResult): object => ({\n schemaVersion: 1,\n ok: true,\n score: result.score,\n summary: result.summary,\n project: {\n name: result.project.packageName,\n rootDirectory: result.project.rootDirectory,\n packageJsonPath: result.project.packageJsonPath,\n lockfiles: result.project.lockfiles,\n legacyLockfiles: result.project.legacyLockfiles,\n },\n diagnostics: result.diagnostics,\n});\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\";\nimport path from \"node:path\";\nimport { VERSION } from \"./constants.js\";\nimport { installSkill } from \"./install-skill.js\";\nimport { formatTextReport, toJsonReport } from \"./report.js\";\nimport { scan } from \"./scan.js\";\nimport type { FailOnLevel, ScanOptions } from \"./types.js\";\n\ninterface CliFlags {\n json: boolean;\n score: boolean;\n verbose: boolean;\n packageChecks: boolean;\n codeChecks: boolean;\n failOn: FailOnLevel;\n}\n\nconst VALID_FAIL_ON_LEVELS = new Set<FailOnLevel>([\"blocker\", \"risk\", \"migration\", \"win\", \"none\"]);\n\nconst HELP_TEXT = `Usage: bun-doctor [directory] [options]\n\nOptions:\n --json output a structured JSON report\n --score output only the numeric score\n --verbose show every diagnostic\n --no-package skip package/config/dependency checks\n --no-code skip source code checks\n --fail-on <level> exit non-zero on blocker, risk, migration, or none\n -v, --version print version\n -h, --help print help\n\nCommands:\n bun-doctor install [directory] [--dry-run]\n`;\n\nconst shouldFail = (levels: Set<string>, failOn: FailOnLevel): boolean => {\n if (failOn === \"none\") return false;\n if (failOn === \"win\") return levels.size > 0;\n if (failOn === \"migration\") return levels.has(\"blocker\") || levels.has(\"risk\") || levels.has(\"migration\");\n if (failOn === \"risk\") return levels.has(\"blocker\") || levels.has(\"risk\");\n return levels.has(\"blocker\");\n};\n\nconst parseFailOn = (value: string | undefined): FailOnLevel => {\n if (!value) return \"blocker\";\n if (VALID_FAIL_ON_LEVELS.has(value as FailOnLevel)) return value as FailOnLevel;\n throw new Error(`Invalid --fail-on value: ${value}. Expected blocker, risk, migration, win, or none.`);\n};\n\nconst parseCli = (argv: string[]): { directory: string; flags: CliFlags } => {\n const parsed = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n json: { type: \"boolean\", default: false },\n score: { type: \"boolean\", default: false },\n verbose: { type: \"boolean\", default: false },\n \"no-package\": { type: \"boolean\", default: false },\n \"no-code\": { type: \"boolean\", default: false },\n \"fail-on\": { type: \"string\", default: \"blocker\" },\n version: { type: \"boolean\", short: \"v\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n });\n\n if (parsed.values.help) {\n process.stdout.write(`${HELP_TEXT}\\n`);\n process.exit(0);\n }\n\n if (parsed.values.version) {\n process.stdout.write(`${VERSION}\\n`);\n process.exit(0);\n }\n\n return {\n directory: path.resolve(parsed.positionals[0] ?? \".\"),\n flags: {\n json: Boolean(parsed.values.json),\n score: Boolean(parsed.values.score),\n verbose: Boolean(parsed.values.verbose),\n packageChecks: !parsed.values[\"no-package\"],\n codeChecks: !parsed.values[\"no-code\"],\n failOn: parseFailOn(parsed.values[\"fail-on\"]),\n },\n };\n};\n\nconst runInstallCommand = (argv: string[]): void => {\n const parsed = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n \"dry-run\": { type: \"boolean\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n });\n\n if (parsed.values.help) {\n process.stdout.write(\"Usage: bun-doctor install [directory] [--dry-run]\\n\");\n return;\n }\n\n const directory = path.resolve(parsed.positionals[0] ?? \".\");\n const skillPath = installSkill({ directory, dryRun: Boolean(parsed.values[\"dry-run\"]) });\n const action = parsed.values[\"dry-run\"] ? \"Would install\" : \"Installed\";\n process.stdout.write(`${action} bun-doctor skill at ${skillPath}\\n`);\n};\n\nconst main = async (): Promise<void> => {\n const argv = process.argv.slice(2);\n if (argv[0] === \"install\") {\n runInstallCommand(argv.slice(1));\n return;\n }\n\n const { directory, flags } = parseCli(argv);\n const options: ScanOptions = {\n packageChecks: flags.packageChecks,\n codeChecks: flags.codeChecks,\n };\n const result = await scan(directory, options);\n\n if (flags.score) {\n process.stdout.write(`${result.score.score}\\n`);\n } else if (flags.json) {\n process.stdout.write(`${JSON.stringify(toJsonReport(result), null, 2)}\\n`);\n } else {\n process.stdout.write(`${formatTextReport(result, flags.verbose)}\\n`);\n }\n\n const levels = new Set(result.diagnostics.map((diagnostic) => diagnostic.level));\n if (shouldFail(levels, flags.failOn)) {\n process.exitCode = 1;\n }\n};\n\nmain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`bun-doctor: ${message}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/install-skill.ts","../src/report.ts","../src/cli.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\ninterface InstallSkillOptions {\n directory: string;\n dryRun?: boolean;\n}\n\nconst SKILL_CONTENT = `---\nname: bun-doctor\ndescription: Use after making dependency, CI, package manager, test runner, or Node runtime changes in a project that uses or is migrating to Bun. Checks Bun readiness and migration risk.\nversion: \"1.0.0\"\n---\n\n# Bun Doctor\n\nRun \\`npx -y bun-doctor@latest . --verbose\\` after Bun migration changes and fix blockers before switching CI or runtime.\n`;\n\nexport const installSkill = (options: InstallSkillOptions): string => {\n const skillDirectory = path.join(options.directory, \".agents\", \"skills\", \"bun-doctor\");\n const skillPath = path.join(skillDirectory, \"SKILL.md\");\n\n if (options.dryRun) return skillPath;\n\n fs.mkdirSync(skillDirectory, { recursive: true });\n fs.writeFileSync(skillPath, SKILL_CONTENT, \"utf8\");\n return skillPath;\n};\n","import path from \"node:path\";\nimport pc from \"picocolors\";\nimport type { Diagnostic, FindingCategory, ScanResult } from \"./types.js\";\nimport { toRelativePath } from \"./utils.js\";\n\nconst CATEGORY_ORDER: FindingCategory[] = [\"Blockers\", \"Risks\", \"Migration work\", \"Bun wins\"];\n\nconst LEVEL_SYMBOL: Record<Diagnostic[\"level\"], string> = {\n blocker: \"x\",\n risk: \"!\",\n migration: \"~\",\n win: \"+\",\n};\n\nconst LEVEL_COLOR: Record<Diagnostic[\"level\"], (input: string) => string> = {\n blocker: pc.red,\n risk: pc.yellow,\n migration: pc.cyan,\n win: pc.green,\n};\n\nconst SCORE_COLOR: Record<ScanResult[\"score\"][\"label\"], (input: string) => string> = {\n Ready: pc.green,\n Close: pc.yellow,\n Risky: pc.magenta,\n Blocked: pc.red,\n};\n\nconst groupByCategory = (diagnostics: Diagnostic[]): Map<FindingCategory, Diagnostic[]> => {\n const groups = new Map<FindingCategory, Diagnostic[]>();\n for (const diagnostic of diagnostics) {\n const existing = groups.get(diagnostic.category) ?? [];\n existing.push(diagnostic);\n groups.set(diagnostic.category, existing);\n }\n return groups;\n};\n\nconst formatLocation = (diagnostic: Diagnostic, rootDirectory: string): string => {\n const relativePath = toRelativePath(path.resolve(diagnostic.filePath), rootDirectory);\n return diagnostic.line > 1 ? `${relativePath}:${diagnostic.line}` : relativePath;\n};\n\nexport const formatTextReport = (result: ScanResult, verbose: boolean): string => {\n const lines: string[] = [];\n const scoreColor = SCORE_COLOR[result.score.label];\n lines.push(pc.bold(\"bun-doctor\"));\n lines.push(`${pc.dim(\"Project:\")} ${result.project.packageName}`);\n lines.push(\n `${pc.dim(\"Bun Readiness:\")} ${scoreColor(pc.bold(`${result.score.score}/100`))} ${pc.dim(`(${result.score.label})`)}`,\n );\n lines.push(\n `${pc.dim(\"Findings:\")} ${pc.red(`${result.summary.blockers} blockers`)}, ${pc.yellow(`${result.summary.risks} risks`)}, ${pc.cyan(`${result.summary.migrations} migration`)}, ${pc.green(`${result.summary.wins} wins`)}`,\n );\n lines.push(\"\");\n\n if (result.diagnostics.length === 0) {\n lines.push(pc.green(\"No Bun migration findings.\"));\n return lines.join(\"\\n\");\n }\n\n const groups = groupByCategory(result.diagnostics);\n for (const category of CATEGORY_ORDER) {\n const diagnostics = groups.get(category) ?? [];\n if (diagnostics.length === 0) continue;\n const firstLevel = diagnostics[0]?.level;\n const categoryColor = firstLevel ? LEVEL_COLOR[firstLevel] : pc.white;\n lines.push(pc.bold(categoryColor(`${category} (${diagnostics.length})`)));\n const shownDiagnostics = verbose ? diagnostics : diagnostics.slice(0, 3);\n for (const diagnostic of shownDiagnostics) {\n const symbol = LEVEL_COLOR[diagnostic.level](LEVEL_SYMBOL[diagnostic.level]);\n lines.push(` ${symbol} ${pc.bold(diagnostic.title)} ${pc.dim(`[${diagnostic.ruleId}]`)}`);\n lines.push(` ${diagnostic.message}`);\n if (diagnostic.replacement) lines.push(` ${pc.green(`→ ${diagnostic.replacement}`)}`);\n if (diagnostic.help) lines.push(` ${pc.dim(diagnostic.help)}`);\n lines.push(` ${pc.cyan(formatLocation(diagnostic, result.project.rootDirectory))}`);\n if (diagnostic.alsoIn && diagnostic.alsoIn.length > 0) {\n const aggregated = diagnostic.alsoIn\n .map((alsoPath) => toRelativePath(path.resolve(alsoPath), result.project.rootDirectory))\n .join(\", \");\n lines.push(` ${pc.dim(`Also in: ${aggregated}`)}`);\n }\n lines.push(` ${pc.dim(`Source: ${diagnostic.sources[0]}`)}`);\n }\n if (!verbose && diagnostics.length > shownDiagnostics.length) {\n const hidden = diagnostics.length - shownDiagnostics.length;\n lines.push(` ${pc.dim(`... ${hidden} more — re-run with --verbose`)}`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\").trimEnd();\n};\n\nexport const toJsonReport = (result: ScanResult): object => ({\n schemaVersion: 1,\n ok: true,\n score: result.score,\n summary: result.summary,\n project: {\n name: result.project.packageName,\n rootDirectory: result.project.rootDirectory,\n packageJsonPath: result.project.packageJsonPath,\n lockfiles: result.project.lockfiles,\n legacyLockfiles: result.project.legacyLockfiles,\n },\n diagnostics: result.diagnostics,\n});\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\";\nimport path from \"node:path\";\nimport { VERSION } from \"./constants.js\";\nimport { installSkill } from \"./install-skill.js\";\nimport { formatTextReport, toJsonReport } from \"./report.js\";\nimport { scan } from \"./scan.js\";\nimport type { FailOnLevel, ScanOptions } from \"./types.js\";\n\ninterface CliFlags {\n json: boolean;\n score: boolean;\n verbose: boolean;\n packageChecks: boolean;\n codeChecks: boolean;\n failOn: FailOnLevel;\n}\n\nconst VALID_FAIL_ON_LEVELS = new Set<FailOnLevel>([\"blocker\", \"risk\", \"migration\", \"win\", \"none\"]);\n\nconst HELP_TEXT = `Usage: bun-doctor [directory] [options]\n\nOptions:\n --json output a structured JSON report\n --score output only the numeric score\n --verbose show every diagnostic\n --no-package skip package/config/dependency checks\n --no-code skip source code checks\n --fail-on <level> exit non-zero on blocker, risk, migration, or none\n -v, --version print version\n -h, --help print help\n\nCommands:\n bun-doctor install [directory] [--dry-run]\n`;\n\nconst shouldFail = (levels: Set<string>, failOn: FailOnLevel): boolean => {\n if (failOn === \"none\") return false;\n if (failOn === \"win\") return levels.size > 0;\n if (failOn === \"migration\") return levels.has(\"blocker\") || levels.has(\"risk\") || levels.has(\"migration\");\n if (failOn === \"risk\") return levels.has(\"blocker\") || levels.has(\"risk\");\n return levels.has(\"blocker\");\n};\n\nconst parseFailOn = (value: string | undefined): FailOnLevel => {\n if (!value) return \"blocker\";\n if (VALID_FAIL_ON_LEVELS.has(value as FailOnLevel)) return value as FailOnLevel;\n throw new Error(`Invalid --fail-on value: ${value}. Expected blocker, risk, migration, win, or none.`);\n};\n\nconst parseCli = (argv: string[]): { directory: string; flags: CliFlags } => {\n const parsed = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n json: { type: \"boolean\", default: false },\n score: { type: \"boolean\", default: false },\n verbose: { type: \"boolean\", default: false },\n \"no-package\": { type: \"boolean\", default: false },\n \"no-code\": { type: \"boolean\", default: false },\n \"fail-on\": { type: \"string\", default: \"blocker\" },\n version: { type: \"boolean\", short: \"v\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n });\n\n if (parsed.values.help) {\n process.stdout.write(`${HELP_TEXT}\\n`);\n process.exit(0);\n }\n\n if (parsed.values.version) {\n process.stdout.write(`${VERSION}\\n`);\n process.exit(0);\n }\n\n return {\n directory: path.resolve(parsed.positionals[0] ?? \".\"),\n flags: {\n json: Boolean(parsed.values.json),\n score: Boolean(parsed.values.score),\n verbose: Boolean(parsed.values.verbose),\n packageChecks: !parsed.values[\"no-package\"],\n codeChecks: !parsed.values[\"no-code\"],\n failOn: parseFailOn(parsed.values[\"fail-on\"]),\n },\n };\n};\n\nconst runInstallCommand = (argv: string[]): void => {\n const parsed = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n \"dry-run\": { type: \"boolean\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n });\n\n if (parsed.values.help) {\n process.stdout.write(\"Usage: bun-doctor install [directory] [--dry-run]\\n\");\n return;\n }\n\n const directory = path.resolve(parsed.positionals[0] ?? \".\");\n const skillPath = installSkill({ directory, dryRun: Boolean(parsed.values[\"dry-run\"]) });\n const action = parsed.values[\"dry-run\"] ? \"Would install\" : \"Installed\";\n process.stdout.write(`${action} bun-doctor skill at ${skillPath}\\n`);\n};\n\nconst main = async (): Promise<void> => {\n const argv = process.argv.slice(2);\n if (argv[0] === \"install\") {\n runInstallCommand(argv.slice(1));\n return;\n }\n\n const { directory, flags } = parseCli(argv);\n const options: ScanOptions = {\n packageChecks: flags.packageChecks,\n codeChecks: flags.codeChecks,\n };\n const result = await scan(directory, options);\n\n if (flags.score) {\n process.stdout.write(`${result.score.score}\\n`);\n } else if (flags.json) {\n process.stdout.write(`${JSON.stringify(toJsonReport(result), null, 2)}\\n`);\n } else {\n process.stdout.write(`${formatTextReport(result, flags.verbose)}\\n`);\n }\n\n const levels = new Set(result.diagnostics.map((diagnostic) => diagnostic.level));\n if (shouldFail(levels, flags.failOn)) {\n process.exitCode = 1;\n }\n};\n\nmain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`bun-doctor: ${message}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -32,13 +32,13 @@ interface BunDoctorConfig {
|
|
|
32
32
|
interface CompatEntry {
|
|
33
33
|
packageName: string;
|
|
34
34
|
severity: FindingLevel;
|
|
35
|
-
affectedRanges: string[];
|
|
36
|
-
bunVersions: string[];
|
|
37
|
-
platforms: Array<"darwin" | "linux" | "win32" | "all">;
|
|
38
35
|
confidence: "high" | "medium" | "low";
|
|
39
36
|
reason: string;
|
|
40
37
|
sources: string[];
|
|
41
38
|
lastVerified: string;
|
|
39
|
+
affectedRanges?: string[];
|
|
40
|
+
bunVersions?: string[];
|
|
41
|
+
platforms?: Array<"darwin" | "linux" | "win32" | "all">;
|
|
42
42
|
replacement?: string;
|
|
43
43
|
workaround?: string;
|
|
44
44
|
migrationHint?: string;
|
|
@@ -73,9 +73,11 @@ interface ProjectInfo {
|
|
|
73
73
|
bunfig: BunfigInfo | null;
|
|
74
74
|
tsconfigPath: string | null;
|
|
75
75
|
tsconfig: Record<string, unknown> | null;
|
|
76
|
+
tsconfigContent: string | null;
|
|
76
77
|
workflows: WorkflowFile[];
|
|
77
78
|
sourceFiles: SourceFile[];
|
|
78
79
|
pnpmWorkspacePath: string | null;
|
|
80
|
+
pnpmWorkspaceContent: string | null;
|
|
79
81
|
}
|
|
80
82
|
interface PackageManifest {
|
|
81
83
|
packageJsonPath: string;
|
|
@@ -83,6 +85,7 @@ interface PackageManifest {
|
|
|
83
85
|
packageName: string;
|
|
84
86
|
dependencies: Record<string, string>;
|
|
85
87
|
trustedDependencies: Set<string>;
|
|
88
|
+
manifestContent: string;
|
|
86
89
|
}
|
|
87
90
|
interface Diagnostic {
|
|
88
91
|
ruleId: string;
|
|
@@ -95,6 +98,8 @@ interface Diagnostic {
|
|
|
95
98
|
sources: string[];
|
|
96
99
|
help?: string;
|
|
97
100
|
packageName?: string;
|
|
101
|
+
replacement?: string;
|
|
102
|
+
alsoIn?: string[];
|
|
98
103
|
}
|
|
99
104
|
interface ScoreResult {
|
|
100
105
|
score: number;
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as calculateScore, r as summarizeDiagnostics, t as scan } from "./scan-
|
|
1
|
+
import { n as calculateScore, r as summarizeDiagnostics, t as scan } from "./scan-BtfKcvQl.mjs";
|
|
2
2
|
export { calculateScore, scan, summarizeDiagnostics };
|