bun-doctor 0.0.3 → 0.0.4

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/bin/bun-doctor.js CHANGED
File without changes
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as VERSION, i as toRelativePath, t as scan } from "./scan-BtfKcvQl.mjs";
2
+ import { a as VERSION, i as toRelativePath, t as scan } from "./scan-JAhbgG60.mjs";
3
3
  import { parseArgs } from "node:util";
4
4
  import path from "node:path";
5
5
  import fs from "node:fs";
@@ -68,7 +68,8 @@ const formatTextReport = (result, verbose) => {
68
68
  lines.push(pc.bold("bun-doctor"));
69
69
  lines.push(`${pc.dim("Project:")} ${result.project.packageName}`);
70
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`)}`);
71
+ const colorCount = (count, color, label) => count === 0 ? pc.dim(`${count} ${label}`) : color(`${count} ${label}`);
72
+ lines.push(`${pc.dim("Findings:")} ${colorCount(result.summary.blockers, pc.red, "blockers")}, ${colorCount(result.summary.risks, pc.yellow, "risks")}, ${colorCount(result.summary.migrations, pc.cyan, "migration")}, ${colorCount(result.summary.wins, pc.green, "wins")}`);
72
73
  lines.push("");
73
74
  if (result.diagnostics.length === 0) {
74
75
  lines.push(pc.green("No Bun migration findings."));
@@ -86,11 +87,14 @@ const formatTextReport = (result, verbose) => {
86
87
  const symbol = LEVEL_COLOR[diagnostic.level](LEVEL_SYMBOL[diagnostic.level]);
87
88
  lines.push(` ${symbol} ${pc.bold(diagnostic.title)} ${pc.dim(`[${diagnostic.ruleId}]`)}`);
88
89
  lines.push(` ${diagnostic.message}`);
89
- if (diagnostic.replacement) lines.push(` ${pc.green(`→ ${diagnostic.replacement}`)}`);
90
+ if (diagnostic.replacement) lines.push(` ${pc.dim("Use")} ${pc.green(diagnostic.replacement)}`);
90
91
  if (diagnostic.help) lines.push(` ${pc.dim(diagnostic.help)}`);
91
92
  lines.push(` ${pc.cyan(formatLocation(diagnostic, result.project.rootDirectory))}`);
92
93
  if (diagnostic.alsoIn && diagnostic.alsoIn.length > 0) {
93
- const aggregated = diagnostic.alsoIn.map((alsoPath) => toRelativePath(path.resolve(alsoPath), result.project.rootDirectory)).join(", ");
94
+ const aggregated = diagnostic.alsoIn.map((alsoLocation) => {
95
+ const relative = toRelativePath(path.resolve(alsoLocation.filePath), result.project.rootDirectory);
96
+ return alsoLocation.line && alsoLocation.line > 1 ? `${relative}:${alsoLocation.line}` : relative;
97
+ }).join(", ");
94
98
  lines.push(` ${pc.dim(`Also in: ${aggregated}`)}`);
95
99
  }
96
100
  lines.push(` ${pc.dim(`Source: ${diagnostic.sources[0]}`)}`);
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 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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"}
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 const colorCount = (count: number, color: (input: string) => string, label: string): string =>\n count === 0 ? pc.dim(`${count} ${label}`) : color(`${count} ${label}`);\n lines.push(\n `${pc.dim(\"Findings:\")} ${colorCount(result.summary.blockers, pc.red, \"blockers\")}, ${colorCount(result.summary.risks, pc.yellow, \"risks\")}, ${colorCount(result.summary.migrations, pc.cyan, \"migration\")}, ${colorCount(result.summary.wins, pc.green, \"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.dim(\"Use\")} ${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((alsoLocation) => {\n const relative = toRelativePath(path.resolve(alsoLocation.filePath), result.project.rootDirectory);\n return alsoLocation.line && alsoLocation.line > 1 ? `${relative}:${alsoLocation.line}` : relative;\n })\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
@@ -99,7 +99,10 @@ interface Diagnostic {
99
99
  help?: string;
100
100
  packageName?: string;
101
101
  replacement?: string;
102
- alsoIn?: string[];
102
+ alsoIn?: Array<{
103
+ filePath: string;
104
+ line?: number;
105
+ }>;
103
106
  }
104
107
  interface ScoreResult {
105
108
  score: number;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as calculateScore, r as summarizeDiagnostics, t as scan } from "./scan-BtfKcvQl.mjs";
1
+ import { n as calculateScore, r as summarizeDiagnostics, t as scan } from "./scan-JAhbgG60.mjs";
2
2
  export { calculateScore, scan, summarizeDiagnostics };
@@ -27,7 +27,7 @@ const BUN_DOCS = {
27
27
  };
28
28
  //#endregion
29
29
  //#region src/constants.ts
30
- const VERSION = "0.0.3";
30
+ const VERSION = "0.0.4";
31
31
  const IGNORED_DIRECTORIES = new Set([
32
32
  ".git",
33
33
  ".next",
@@ -944,7 +944,10 @@ const runPackageRules = (project) => {
944
944
  if (!primaryManifest) continue;
945
945
  const isTrustedEverywhere = matchingManifests.every((manifest) => manifest.trustedDependencies.has(entry.packageName));
946
946
  const needsTrust = Boolean(entry.requiresTrustedDependency) && !isTrustedEverywhere;
947
- const alsoIn = otherManifests.map((manifest) => manifest.packageJsonPath);
947
+ const alsoIn = otherManifests.map((manifest) => ({
948
+ filePath: manifest.packageJsonPath,
949
+ line: findDependencyLine(manifest.manifestContent, entry.packageName)
950
+ }));
948
951
  diagnostics.push(createCompatDiagnostic(entry, primaryManifest, needsTrust, alsoIn));
949
952
  continue;
950
953
  }
@@ -1089,12 +1092,30 @@ const summarizeDiagnostics = (diagnostics) => ({
1089
1092
  });
1090
1093
  //#endregion
1091
1094
  //#region src/scan.ts
1095
+ const collapseDuplicateRules = (diagnostics) => {
1096
+ const seenByRuleId = /* @__PURE__ */ new Map();
1097
+ for (const diagnostic of diagnostics) {
1098
+ const existing = seenByRuleId.get(diagnostic.ruleId);
1099
+ if (!existing) {
1100
+ seenByRuleId.set(diagnostic.ruleId, diagnostic);
1101
+ continue;
1102
+ }
1103
+ seenByRuleId.set(diagnostic.ruleId, {
1104
+ ...existing,
1105
+ alsoIn: [...existing.alsoIn ?? [], {
1106
+ filePath: diagnostic.filePath,
1107
+ line: diagnostic.line
1108
+ }]
1109
+ });
1110
+ }
1111
+ return [...seenByRuleId.values()];
1112
+ };
1092
1113
  const scan = async (directory, options = {}) => {
1093
1114
  const project = discoverProject(directory);
1094
1115
  const loadedConfig = options.configOverride ?? loadConfig(project.rootDirectory, project.packageJson);
1095
1116
  const shouldRunPackageChecks = options.packageChecks ?? loadedConfig.package ?? true;
1096
1117
  const shouldRunCodeChecks = options.codeChecks ?? loadedConfig.code ?? true;
1097
- const filteredDiagnostics = filterIgnoredDiagnostics([...shouldRunPackageChecks ? runPackageRules(project) : [], ...shouldRunCodeChecks ? runCodeRules(project) : []], loadedConfig, project.rootDirectory);
1118
+ const filteredDiagnostics = filterIgnoredDiagnostics(collapseDuplicateRules([...shouldRunPackageChecks ? runPackageRules(project) : [], ...shouldRunCodeChecks ? runCodeRules(project) : []]), loadedConfig, project.rootDirectory);
1098
1119
  return {
1099
1120
  project,
1100
1121
  diagnostics: filteredDiagnostics,
@@ -1105,4 +1126,4 @@ const scan = async (directory, options = {}) => {
1105
1126
  //#endregion
1106
1127
  export { VERSION as a, toRelativePath as i, calculateScore as n, summarizeDiagnostics as r, scan as t };
1107
1128
 
1108
- //# sourceMappingURL=scan-BtfKcvQl.mjs.map
1129
+ //# sourceMappingURL=scan-JAhbgG60.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-JAhbgG60.mjs","names":["parseToml"],"sources":["../src/bun-docs.ts","../src/constants.ts","../src/utils.ts","../src/config.ts","../src/project.ts","../src/compat-db.ts","../src/rules.ts","../src/score.ts","../src/scan.ts"],"sourcesContent":["// Generated by scripts/refresh-bun-docs.ts.\n// Do not edit by hand — run `bun run docs:refresh` to update.\n\nexport const BUN_DOCS = {\n autoInstall: \"https://bun.com/docs/runtime/auto-install\",\n bundler: \"https://bun.com/docs/bundler\",\n bundlerEsbuild: \"https://bun.com/docs/bundler/esbuild\",\n bunfig: \"https://bun.com/docs/runtime/bunfig\",\n catalogs: \"https://bun.com/docs/pm/catalogs\",\n ci: \"https://bun.com/docs/guides/install/cicd\",\n environmentVariables: \"https://bun.com/docs/runtime/environment-variables\",\n fetch: \"https://bun.com/docs/runtime/networking/fetch\",\n hashing: \"https://bun.com/docs/runtime/hashing\",\n lifecycle: \"https://bun.com/docs/pm/lifecycle\",\n lockfile: \"https://bun.com/docs/pm/lockfile\",\n nodeCompatibility: \"https://bun.com/docs/runtime/nodejs-compat\",\n plugins: \"https://bun.com/docs/runtime/plugins\",\n redis: \"https://bun.com/docs/runtime/redis\",\n securityScanner: \"https://bun.com/docs/pm/security-scanner-api\",\n sqlite: \"https://bun.com/docs/runtime/sqlite\",\n testConfiguration: \"https://bun.com/docs/test/configuration\",\n testRunner: \"https://bun.com/docs/test\",\n typescript: \"https://bun.com/docs/typescript\",\n watchMode: \"https://bun.com/docs/runtime/watch-mode\",\n workspaces: \"https://bun.com/docs/pm/workspaces\",\n};\n","export const VERSION = process.env.VERSION ?? \"0.0.0\";\n\nexport const PERFECT_SCORE = 100;\nexport const BLOCKER_RULE_PENALTY = 12;\nexport const RISK_RULE_PENALTY = 5;\nexport const MIGRATION_RULE_PENALTY = 2;\n\nexport const SCORE_READY_THRESHOLD = 90;\nexport const SCORE_CLOSE_THRESHOLD = 75;\nexport const SCORE_RISKY_THRESHOLD = 50;\n\nexport { BUN_DOCS } from \"./bun-docs.js\";\n\nexport const IGNORED_DIRECTORIES = new Set([\n \".git\",\n \".next\",\n \".turbo\",\n \"build\",\n \"coverage\",\n \"dist\",\n \"node_modules\",\n \"vendor\",\n]);\n\nexport const SOURCE_FILE_EXTENSIONS = new Set([\n \".cjs\",\n \".cts\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".mts\",\n \".ts\",\n \".tsx\",\n]);\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { IGNORED_DIRECTORIES, SOURCE_FILE_EXTENSIONS } from \"./constants.js\";\n\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport const fileExists = (filePath: string): boolean => {\n try {\n return fs.statSync(filePath).isFile();\n } catch {\n return false;\n }\n};\n\nexport const directoryExists = (directoryPath: string): boolean => {\n try {\n return fs.statSync(directoryPath).isDirectory();\n } catch {\n return false;\n }\n};\n\nexport const readJsonFile = <T>(filePath: string): T | null => {\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf8\")) as T;\n } catch {\n return null;\n }\n};\n\nexport const collectFiles = (rootDirectory: string, predicate: (filePath: string) => boolean): string[] => {\n const files: string[] = [];\n const stack = [rootDirectory];\n\n while (stack.length > 0) {\n const currentDirectory = stack.pop();\n if (!currentDirectory) continue;\n\n let entries: fs.Dirent[] = [];\n try {\n entries = fs.readdirSync(currentDirectory, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n const entryPath = path.join(currentDirectory, entry.name);\n if (entry.isDirectory()) {\n if (!entry.name.startsWith(\".\") && !IGNORED_DIRECTORIES.has(entry.name)) {\n stack.push(entryPath);\n }\n continue;\n }\n if (entry.isFile() && predicate(entryPath)) {\n files.push(entryPath);\n }\n }\n }\n\n return files.sort();\n};\n\nexport const isSourceFilePath = (filePath: string): boolean => {\n if (filePath.endsWith(\".d.ts\")) return false;\n return SOURCE_FILE_EXTENSIONS.has(path.extname(filePath));\n};\n\nexport const toRelativePath = (filePath: string, rootDirectory: string): string =>\n path.relative(rootDirectory, filePath).replaceAll(path.sep, \"/\") || \".\";\n\nexport const findLineNumber = (content: string, pattern: RegExp): number => {\n const lines = content.split(/\\r?\\n/);\n const flags = pattern.flags.replace(\"g\", \"\");\n const linePattern = new RegExp(pattern.source, flags);\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n if (linePattern.test(lines[lineIndex] ?? \"\")) return lineIndex + 1;\n }\n\n return 1;\n};\n\nexport const escapeRegExp = (text: string): string => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\nexport const wildcardToRegExp = (pattern: string): RegExp => {\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replaceAll(\"**\", \"__DOUBLE_STAR__\")\n .replaceAll(\"*\", \"[^/]*\")\n .replaceAll(\"__DOUBLE_STAR__\", \".*\");\n return new RegExp(`^${escaped}$`);\n};\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { BunDoctorConfig, Diagnostic, PackageJson } from \"./types.js\";\nimport { readJsonFile, toRelativePath, wildcardToRegExp } from \"./utils.js\";\n\nconst CONFIG_FILE_NAME = \"bun-doctor.config.json\";\n\nexport const loadConfig = (rootDirectory: string, packageJson: PackageJson): BunDoctorConfig => {\n const configPath = path.join(rootDirectory, CONFIG_FILE_NAME);\n if (fs.existsSync(configPath)) {\n return readJsonFile<BunDoctorConfig>(configPath) ?? {};\n }\n return packageJson.bunDoctor ?? {};\n};\n\nexport const filterIgnoredDiagnostics = (\n diagnostics: Diagnostic[],\n config: BunDoctorConfig,\n rootDirectory: string,\n): Diagnostic[] => {\n const ignoredRules = new Set(config.ignore?.rules ?? []);\n const ignoredFilePatterns = (config.ignore?.files ?? []).map(wildcardToRegExp);\n\n return diagnostics.filter((diagnostic) => {\n if (ignoredRules.has(diagnostic.ruleId)) return false;\n const relativePath = toRelativePath(diagnostic.filePath, rootDirectory);\n return !ignoredFilePatterns.some((pattern) => pattern.test(relativePath));\n });\n};\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport type {\n BunfigInfo,\n PackageJson,\n PackageManifest,\n ProjectInfo,\n SourceFile,\n WorkflowFile,\n} from \"./types.js\";\nimport { collectFiles, fileExists, isPlainObject, isSourceFilePath, readJsonFile, wildcardToRegExp } from \"./utils.js\";\n\nconst LOCKFILE_NAMES = [\"bun.lock\", \"bun.lockb\"];\nconst LEGACY_LOCKFILE_NAMES = [\"package-lock.json\", \"pnpm-lock.yaml\", \"yarn.lock\"];\nconst WORKFLOW_EXTENSIONS = new Set([\".yml\", \".yaml\"]);\n\nconst collectDependencies = (packageJson: PackageJson): Record<string, string> => ({\n ...packageJson.optionalDependencies,\n ...packageJson.peerDependencies,\n ...packageJson.devDependencies,\n ...packageJson.dependencies,\n});\n\nconst readManifestContent = (packageJsonPath: string): string => {\n try {\n return fs.readFileSync(packageJsonPath, \"utf8\");\n } catch {\n return \"\";\n }\n};\n\nconst toPackageManifest = (packageJsonPath: string, packageJson: PackageJson): PackageManifest => ({\n packageJsonPath,\n packageJson,\n packageName: packageJson.name ?? path.basename(path.dirname(packageJsonPath)),\n dependencies: collectDependencies(packageJson),\n trustedDependencies: new Set(packageJson.trustedDependencies ?? []),\n manifestContent: readManifestContent(packageJsonPath),\n});\n\nconst extractPackageJsonWorkspaceGlobs = (packageJson: PackageJson): string[] => {\n const workspaces = packageJson.workspaces;\n if (Array.isArray(workspaces)) return workspaces;\n if (isPlainObject(workspaces) && Array.isArray(workspaces.packages)) return workspaces.packages as string[];\n return [];\n};\n\nconst parsePnpmWorkspaceGlobs = (content: string): string[] => {\n const globs: string[] = [];\n let inPackagesBlock = false;\n let packagesIndent = -1;\n for (const rawLine of content.split(/\\r?\\n/)) {\n if (/^\\s*#/.test(rawLine)) continue;\n if (rawLine.trim() === \"\") continue;\n const leadingWhitespace = rawLine.length - rawLine.trimStart().length;\n if (/^packages\\s*:\\s*$/.test(rawLine.trimEnd())) {\n inPackagesBlock = true;\n packagesIndent = leadingWhitespace;\n continue;\n }\n if (!inPackagesBlock) continue;\n if (leadingWhitespace <= packagesIndent) {\n inPackagesBlock = false;\n continue;\n }\n const itemMatch = rawLine.match(/^\\s*-\\s*[\"']?([^\"']+?)[\"']?\\s*$/);\n if (itemMatch?.[1]) globs.push(itemMatch[1]);\n }\n return globs;\n};\n\ninterface SplitGlobs {\n positive: string[];\n negative: string[];\n}\n\nconst splitGlobs = (globs: string[]): SplitGlobs => {\n const positive: string[] = [];\n const negative: string[] = [];\n for (const glob of globs) {\n if (glob.startsWith(\"!\")) negative.push(glob.slice(1));\n else positive.push(glob);\n }\n return { positive, negative };\n};\n\nconst pathMatchesAnyGlob = (relativePath: string, globs: string[]): boolean => {\n for (const glob of globs) {\n if (wildcardToRegExp(glob).test(relativePath)) return true;\n }\n return false;\n};\n\nconst isWorkspacePath = (relativePath: string, splitWorkspaceGlobs: SplitGlobs): boolean =>\n pathMatchesAnyGlob(relativePath, splitWorkspaceGlobs.positive) &&\n !pathMatchesAnyGlob(relativePath, splitWorkspaceGlobs.negative);\n\nconst collectPackageManifests = (\n rootDirectory: string,\n rootPackageJsonPath: string,\n rootPackageJson: PackageJson,\n workspaceGlobs: string[],\n): PackageManifest[] => {\n const rootManifest = toPackageManifest(rootPackageJsonPath, rootPackageJson);\n if (workspaceGlobs.length === 0) return [rootManifest];\n\n const splitWorkspaceGlobs = splitGlobs(workspaceGlobs);\n if (splitWorkspaceGlobs.positive.length === 0) return [rootManifest];\n\n const manifestPaths = collectFiles(\n rootDirectory,\n (filePath) => path.basename(filePath) === \"package.json\" && filePath !== rootPackageJsonPath,\n );\n const workspaceManifests = manifestPaths.flatMap((manifestPath) => {\n const relativeDirectory = path\n .relative(rootDirectory, path.dirname(manifestPath))\n .replaceAll(path.sep, \"/\");\n if (!isWorkspacePath(relativeDirectory, splitWorkspaceGlobs)) return [];\n const packageJson = readJsonFile<PackageJson>(manifestPath);\n if (!packageJson || typeof packageJson !== \"object\" || Array.isArray(packageJson)) return [];\n return [toPackageManifest(manifestPath, packageJson)];\n });\n return [rootManifest, ...workspaceManifests];\n};\n\nconst mergeDependencies = (manifests: PackageManifest[]): Record<string, string> => {\n const dependencies: Record<string, string> = {};\n for (const manifest of manifests) {\n Object.assign(dependencies, manifest.dependencies);\n }\n return dependencies;\n};\n\nconst mergeTrustedDependencies = (manifests: PackageManifest[]): Set<string> => {\n const trustedDependencies = new Set<string>();\n for (const manifest of manifests) {\n for (const packageName of manifest.trustedDependencies) {\n trustedDependencies.add(packageName);\n }\n }\n return trustedDependencies;\n};\n\nconst getTomlSection = (parsed: Record<string, unknown>, sectionPath: string[]): Record<string, unknown> | null => {\n let current: unknown = parsed;\n for (const segment of sectionPath) {\n if (!isPlainObject(current)) return null;\n current = (current as Record<string, unknown>)[segment];\n }\n return isPlainObject(current) ? (current as Record<string, unknown>) : null;\n};\n\nconst readBooleanField = (section: Record<string, unknown> | null, key: string): boolean | undefined => {\n const value = section?.[key];\n return typeof value === \"boolean\" ? value : undefined;\n};\n\nconst readStringField = (section: Record<string, unknown> | null, key: string): string | undefined => {\n const value = section?.[key];\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst parseBunfig = (rootDirectory: string): BunfigInfo | null => {\n const filePath = path.join(rootDirectory, \"bunfig.toml\");\n if (!fileExists(filePath)) return null;\n const content = fs.readFileSync(filePath, \"utf8\");\n\n let parsed: Record<string, unknown>;\n try {\n parsed = parseToml(content) as Record<string, unknown>;\n } catch {\n return { filePath, content };\n }\n\n const installSection = getTomlSection(parsed, [\"install\"]);\n const securitySection = getTomlSection(parsed, [\"install\", \"security\"]);\n\n return {\n filePath,\n content,\n installIgnoreScripts: readBooleanField(installSection, \"ignoreScripts\"),\n installFrozenLockfile: readBooleanField(installSection, \"frozenLockfile\"),\n installAuto: readStringField(installSection, \"auto\"),\n installSecurityScanner: readStringField(securitySection, \"scanner\"),\n };\n};\n\nconst readSourceFiles = (rootDirectory: string): SourceFile[] =>\n collectFiles(rootDirectory, isSourceFilePath).map((filePath) => ({\n filePath,\n content: fs.readFileSync(filePath, \"utf8\"),\n }));\n\nconst readWorkflowFiles = (rootDirectory: string): WorkflowFile[] => {\n const workflowDirectory = path.join(rootDirectory, \".github\", \"workflows\");\n if (!fs.existsSync(workflowDirectory)) return [];\n return collectFiles(workflowDirectory, (filePath) => WORKFLOW_EXTENSIONS.has(path.extname(filePath))).map(\n (filePath) => ({ filePath, content: fs.readFileSync(filePath, \"utf8\") }),\n );\n};\n\nconst readTsconfig = (\n rootDirectory: string,\n): { path: string | null; config: Record<string, unknown> | null; content: string | null } => {\n const tsconfigPath = path.join(rootDirectory, \"tsconfig.json\");\n if (!fileExists(tsconfigPath)) return { path: null, config: null, content: null };\n const config = readJsonFile<Record<string, unknown>>(tsconfigPath);\n let content: string | null = null;\n try {\n content = fs.readFileSync(tsconfigPath, \"utf8\");\n } catch {\n content = null;\n }\n return { path: tsconfigPath, config, content };\n};\n\nconst findPackageJsonPath = (startDirectory: string): string => {\n const packageJsonPath = path.join(startDirectory, \"package.json\");\n if (fileExists(packageJsonPath)) return packageJsonPath;\n throw new Error(`No package.json found in ${startDirectory}`);\n};\n\nexport const discoverProject = (directory: string): ProjectInfo => {\n const rootDirectory = path.resolve(directory);\n const packageJsonPath = findPackageJsonPath(rootDirectory);\n const packageJson = readJsonFile<PackageJson>(packageJsonPath);\n if (!packageJson || typeof packageJson !== \"object\" || Array.isArray(packageJson)) {\n throw new Error(`Could not parse ${packageJsonPath}`);\n }\n\n const lockfiles = LOCKFILE_NAMES.filter((lockfileName) => fileExists(path.join(rootDirectory, lockfileName)));\n const legacyLockfiles = LEGACY_LOCKFILE_NAMES.filter((lockfileName) =>\n fileExists(path.join(rootDirectory, lockfileName)),\n );\n const pnpmWorkspacePath = path.join(rootDirectory, \"pnpm-workspace.yaml\");\n const pnpmWorkspaceContent = fileExists(pnpmWorkspacePath) ? fs.readFileSync(pnpmWorkspacePath, \"utf8\") : null;\n const workspaceGlobs = [\n ...extractPackageJsonWorkspaceGlobs(packageJson),\n ...(pnpmWorkspaceContent ? parsePnpmWorkspaceGlobs(pnpmWorkspaceContent) : []),\n ];\n const packageManifests = collectPackageManifests(rootDirectory, packageJsonPath, packageJson, workspaceGlobs);\n const tsconfig = readTsconfig(rootDirectory);\n\n return {\n rootDirectory,\n packageJsonPath,\n packageJson,\n packageName: packageJson.name ?? path.basename(rootDirectory),\n dependencies: mergeDependencies(packageManifests),\n trustedDependencies: mergeTrustedDependencies(packageManifests),\n packageManifests,\n lockfiles,\n legacyLockfiles,\n bunfig: parseBunfig(rootDirectory),\n tsconfigPath: tsconfig.path,\n tsconfig: tsconfig.config,\n tsconfigContent: tsconfig.content,\n workflows: readWorkflowFiles(rootDirectory),\n sourceFiles: readSourceFiles(rootDirectory),\n pnpmWorkspacePath: pnpmWorkspaceContent ? pnpmWorkspacePath : null,\n pnpmWorkspaceContent,\n };\n};\n","import { BUN_DOCS } from \"./constants.js\";\nimport type { CompatEntry } from \"./types.js\";\n\nconst LAST_VERIFIED = \"2026-05-12\";\n\nexport const COMPAT_DB: CompatEntry[] = [\n {\n packageName: \"@napi-rs/canvas\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"@napi-rs/canvas ships prebuilt N-API binaries that generally work on Bun without lifecycle script gymnastics, unlike node-canvas.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n migrationHint:\n \"Prefer @napi-rs/canvas over node-canvas when migrating to Bun for a cleaner install path.\",\n },\n {\n packageName: \"@prisma/client\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"Prisma Client generates code via a postinstall script that Bun skips for dependencies by default.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add @prisma/client to trustedDependencies, or run prisma generate explicitly in the build step.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"@swc-node/register\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun runs TypeScript directly, making @swc-node/register redundant when Bun is the target runtime.\",\n sources: [BUN_DOCS.typescript],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun run file.ts\",\n migrationHint: \"Remove @swc-node/register loader flags from Bun-targeted scripts.\",\n },\n {\n packageName: \"argon2\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Bun.password supports argon2 natively, and the argon2 npm package's native install depends on lifecycle scripts Bun skips by default.\",\n sources: [BUN_DOCS.hashing, BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.password\",\n migrationHint:\n \"Consider Bun.password with algorithm 'argon2id' for new Bun-only password hashing code.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"ava\",\n severity: \"win\",\n confidence: \"low\",\n reason:\n \"AVA runs on Bun, but its test paradigm differs from bun:test enough that migration is real work rather than a script swap.\",\n sources: [BUN_DOCS.testRunner],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:test\",\n workaround:\n \"Keep AVA if its assertion model or concurrency behavior matters; otherwise bun:test is the Bun-native alternative.\",\n },\n {\n packageName: \"axios\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun ships native fetch and Web HTTP APIs that cover most axios use cases, though axios continues to work on Bun.\",\n sources: [BUN_DOCS.fetch, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"global fetch\",\n migrationHint:\n \"Evaluate global fetch for simple JSON flows; keep axios where you rely on its interceptors, defaults, or error model.\",\n },\n {\n packageName: \"bcrypt\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Bun.password provides native bcrypt hashing, and the bcrypt npm package's native install requires lifecycle scripts Bun skips by default.\",\n sources: [BUN_DOCS.hashing, BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.password\",\n migrationHint:\n \"Bun.password is a drop-in replacement for most bcrypt.hash/compare flows when Bun is the target runtime.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"bcryptjs\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"bcryptjs runs fine on Bun, but Bun.password offers a native bcrypt implementation that is typically much faster.\",\n sources: [BUN_DOCS.hashing],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.password\",\n migrationHint:\n \"Bun.password covers most bcryptjs use cases with better performance when targeting Bun.\",\n },\n {\n packageName: \"better-sqlite3\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"better-sqlite3 uses native bindings and lifecycle scripts that Bun skips for dependencies by default, and Bun ships a native SQLite driver.\",\n sources: [BUN_DOCS.sqlite, BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:sqlite\",\n workaround:\n \"If you keep better-sqlite3, add it to trustedDependencies and verify install on every target platform.\",\n migrationHint:\n \"Database from bun:sqlite covers the synchronous-driver use case better-sqlite3 is usually chosen for.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"chokidar\",\n severity: \"win\",\n confidence: \"low\",\n reason:\n \"chokidar runs on Bun via fs.watch fallbacks, but Bun's --watch mode and fs.watch cover most file-watcher use cases natively.\",\n sources: [BUN_DOCS.watchMode, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Keep chokidar if you rely on its cross-platform glob semantics or stability heuristics; otherwise evaluate native fs.watch.\",\n },\n {\n packageName: \"canvas\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"node-canvas relies on native bindings and platform-specific install steps that depend on Bun's lifecycle-script handling.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add canvas to trustedDependencies and verify installs on each target OS, or evaluate @napi-rs/canvas if its API fits.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"cross-fetch\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun provides native Request, Response, Headers, and fetch, making cross-fetch redundant as a polyfill.\",\n sources: [BUN_DOCS.fetch, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"global fetch\",\n migrationHint:\n \"Remove cross-fetch when all target runtimes (including Bun) provide global fetch.\",\n },\n {\n packageName: \"cypress\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"Cypress installs its desktop binary via a postinstall script that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add cypress to trustedDependencies, or run npx cypress install explicitly after bun install.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"dotenv\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun loads .env, .env.local, and environment-specific .env files automatically, making dotenv redundant for the files Bun already reads.\",\n sources: [BUN_DOCS.environmentVariables],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun built-in .env loading\",\n migrationHint:\n \"Remove dotenv/config bootstraps when Bun's automatic .env loading already covers the same files.\",\n },\n {\n packageName: \"esbuild\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Bun ships a native bundler with an esbuild migration guide for projects that only need basic bundling.\",\n sources: [BUN_DOCS.bundler, BUN_DOCS.bundlerEsbuild],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun build\",\n migrationHint:\n \"Evaluate bun build for build scripts whose esbuild config is straightforward (entry + format + outdir).\",\n },\n {\n packageName: \"got\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun ships native fetch and Web HTTP APIs that cover most got use cases, though got continues to work on Bun.\",\n sources: [BUN_DOCS.fetch, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"global fetch\",\n migrationHint:\n \"Evaluate global fetch for simple HTTP flows; keep got where you rely on its retry or hooks model.\",\n },\n {\n packageName: \"ioredis\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"ioredis works on Bun, but Bun ships a native Redis client that avoids the parser/IO overhead of a pure-JS client.\",\n sources: [BUN_DOCS.redis],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.RedisClient\",\n workaround:\n \"Keep ioredis if you rely on its cluster, sentinel, or scripting helpers; otherwise evaluate the native Bun Redis client.\",\n },\n {\n packageName: \"jest\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Jest works on Bun, and bun:test ships natively with Jest-compatible expect/describe/test for most suites.\",\n sources: [BUN_DOCS.testRunner, BUN_DOCS.testConfiguration],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:test\",\n workaround:\n \"Keep Jest if you rely on jest.config transforms, custom resolvers, or jest-specific reporter behavior.\",\n migrationHint:\n \"Audit jest.config and setup files before switching test scripts to bun test.\",\n },\n {\n packageName: \"mocha\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Mocha runs on Bun, but bun:test is the Jest-compatible native alternative that requires rewriting BDD-style suites to adopt.\",\n sources: [BUN_DOCS.testRunner],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:test\",\n workaround:\n \"Keep Mocha if its BDD style or reporter ecosystem matters to the project.\",\n },\n {\n packageName: \"nan\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"nan is the legacy Node Abstractions header used by older native modules; modules depending on it require lifecycle scripts Bun skips unless trusted.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Audit which dependencies pull in nan and add them to trustedDependencies after verifying installs on each target platform.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"node-fetch\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun provides global fetch, making node-fetch redundant as a polyfill.\",\n sources: [BUN_DOCS.fetch, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"global fetch\",\n migrationHint:\n \"Remove node-fetch imports when all target runtimes provide global fetch.\",\n },\n {\n packageName: \"node-gyp\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"node-gyp drives native module builds via lifecycle scripts that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Audit which dependencies need node-gyp at install time and add them to trustedDependencies after verifying on each target platform.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"node-pty\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"node-pty wraps native pseudo-terminal bindings via node-gyp and depends on lifecycle scripts Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add node-pty to trustedDependencies and verify installs on each target OS, especially Windows.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"node-pre-gyp\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"node-pre-gyp resolves prebuilt native binaries via lifecycle scripts that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add affected packages to trustedDependencies, or verify that prebuilt binaries resolve without an install step.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"node-sass\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"node-sass depends on native install steps Bun skips by default and is upstream-deprecated in favor of sass.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n replacement: \"sass\",\n workaround:\n \"Prefer Dart Sass (sass); if you keep node-sass, add it to trustedDependencies and verify platform installs.\",\n migrationHint: \"Replace node-sass with sass where possible.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"nodemon\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun has built-in --watch and --hot modes that cover most nodemon dev-script use cases.\",\n sources: [BUN_DOCS.watchMode],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun --watch or bun --hot\",\n migrationHint:\n \"Replace nodemon dev scripts with bun --watch or bun --hot when their behavior matches.\",\n },\n {\n packageName: \"pm2\",\n severity: \"win\",\n confidence: \"low\",\n reason:\n \"pm2 runs on Bun but is heavier than Bun's --watch/--hot modes for dev loops; production process management remains pm2's strength.\",\n sources: [BUN_DOCS.watchMode],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Keep pm2 for cluster mode, log rotation, or zero-downtime restarts; replace its dev-watch usage with bun --watch.\",\n },\n {\n packageName: \"pg-native\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"pg-native links to libpq and requires native install steps Bun skips by default, while the pure-JS pg client has no such constraint.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"pg\",\n workaround:\n \"If pg-native is required for performance, add it to trustedDependencies and verify install on every target platform.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"playwright\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"Playwright downloads browser binaries via a postinstall script that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add playwright (and browser packages) to trustedDependencies, or run npx playwright install explicitly in CI.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"prisma\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"The Prisma CLI runs schema generation via lifecycle scripts that Bun skips for dependencies by default.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add prisma to trustedDependencies, or run prisma generate explicitly in the build step.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"puppeteer\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"puppeteer downloads Chromium via a postinstall script that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add puppeteer to trustedDependencies, install Chromium manually, or use puppeteer-core with an existing browser binary.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"redis\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"node-redis (the redis package) works on Bun, but Bun ships a native Redis client that avoids the parser overhead of a pure-JS client.\",\n sources: [BUN_DOCS.redis],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.RedisClient\",\n workaround:\n \"Keep node-redis if you rely on its v4 command/feature surface; otherwise evaluate the native Bun Redis client.\",\n },\n {\n packageName: \"serialport\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"serialport ships native bindings whose install paths depend on lifecycle scripts Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add serialport to trustedDependencies and verify installs on each target OS and Node-API ABI.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"sharp\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"sharp uses native bindings and prebuilds whose install behavior depends on Bun's lifecycle-script handling.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add sharp to trustedDependencies and verify installs on each target OS and arch (especially Alpine/musl).\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"sqlite3\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"sqlite3 uses native bindings and lifecycle scripts Bun skips by default, while Bun ships a native SQLite driver.\",\n sources: [BUN_DOCS.sqlite, BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:sqlite\",\n migrationHint:\n \"Database from bun:sqlite covers most sqlite3 use cases for new Bun-only code.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"ts-node\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun executes TypeScript files directly, making ts-node redundant when Bun is the runtime.\",\n sources: [BUN_DOCS.typescript],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun run file.ts\",\n migrationHint:\n \"Replace ts-node scripts with bun run and drop --loader ts-node/esm flags from Bun-targeted scripts.\",\n },\n {\n packageName: \"tsup\",\n severity: \"win\",\n confidence: \"low\",\n reason:\n \"tsup wraps esbuild and works on Bun; Bun's native bundler covers many tsup use cases for libraries with simple build configs.\",\n sources: [BUN_DOCS.bundler, BUN_DOCS.bundlerEsbuild],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun build\",\n workaround:\n \"Keep tsup where you rely on its dts generation, watch mode, or specific build target presets.\",\n },\n {\n packageName: \"tsx\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun runs TypeScript files directly with --watch and --hot modes that duplicate tsx's functionality.\",\n sources: [BUN_DOCS.typescript, BUN_DOCS.watchMode],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun run file.ts\",\n migrationHint: \"Replace tsx scripts with bun run when Bun is the target runtime.\",\n },\n {\n packageName: \"vitest\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Vitest runs on Bun, but bun:test is the Jest-compatible native alternative whose suitability depends on which Vitest features the suite uses.\",\n sources: [BUN_DOCS.testRunner],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:test\",\n workaround:\n \"Keep Vitest if you rely on its Vite plugin pipeline, browser mode, or specific snapshot/coverage behavior.\",\n },\n {\n packageName: \"webpack\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Bun includes a native bundler that covers many application and library workflows webpack handles.\",\n sources: [BUN_DOCS.bundler],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun build\",\n migrationHint:\n \"Evaluate bun build for simple library/app bundles; keep webpack where you rely on loaders or plugins bun build doesn't cover.\",\n },\n];\n","import path from \"node:path\";\nimport { BUN_DOCS } from \"./constants.js\";\nimport { COMPAT_DB } from \"./compat-db.js\";\nimport type {\n CompatEntry,\n Diagnostic,\n FindingCategory,\n FindingLevel,\n PackageManifest,\n ProjectInfo,\n} from \"./types.js\";\nimport { escapeRegExp, findLineNumber, isPlainObject } from \"./utils.js\";\n\nconst CATEGORY_BY_LEVEL: Record<FindingLevel, FindingCategory> = {\n blocker: \"Blockers\",\n risk: \"Risks\",\n migration: \"Migration work\",\n win: \"Bun wins\",\n};\n\ninterface DiagnosticInput {\n ruleId: string;\n title: string;\n level: FindingLevel;\n message: string;\n filePath: string;\n sources: string[];\n line?: number;\n help?: string;\n packageName?: string;\n replacement?: string;\n alsoIn?: Array<{ filePath: string; line?: number }>;\n}\n\ninterface CodeRule {\n ruleId: string;\n title: string;\n level: FindingLevel;\n pattern: RegExp;\n requiresPattern?: RegExp;\n message: string;\n sources: string[];\n help?: string;\n}\n\nconst createDiagnostic = (input: DiagnosticInput): Diagnostic => {\n if (input.sources.length === 0) {\n throw new Error(`Rule ${input.ruleId} is missing a source`);\n }\n return {\n ruleId: input.ruleId,\n title: input.title,\n level: input.level,\n category: CATEGORY_BY_LEVEL[input.level],\n message: input.message,\n filePath: input.filePath,\n line: input.line ?? 1,\n sources: input.sources,\n help: input.help,\n packageName: input.packageName,\n replacement: input.replacement,\n alsoIn: input.alsoIn,\n };\n};\n\nconst buildCompatHelp = (entry: CompatEntry, needsTrust: boolean): string | undefined => {\n if (needsTrust) {\n return `Add ${entry.packageName} to trustedDependencies before relying on its install scripts.`;\n }\n return entry.migrationHint ?? entry.workaround;\n};\n\nconst findKeyLine = (content: string | null, key: string): number => {\n if (!content) return 1;\n return findLineNumber(content, new RegExp(`\"${escapeRegExp(key)}\"\\\\s*:`));\n};\n\nconst findYamlKeyLine = (content: string | null, key: string): number => {\n if (!content) return 1;\n return findLineNumber(content, new RegExp(`^\\\\s*${escapeRegExp(key)}\\\\s*:`, \"m\"));\n};\n\nconst findCatalogReferenceLine = (manifestContent: string | null): number => {\n if (!manifestContent) return 1;\n return findLineNumber(manifestContent, /\"catalog:/);\n};\n\nconst findDependencyLine = (manifestContent: string, packageName: string): number =>\n findKeyLine(manifestContent, packageName);\n\nconst createCompatDiagnostic = (\n entry: CompatEntry,\n manifest: PackageManifest,\n needsTrust: boolean,\n alsoIn?: Array<{ filePath: string; line?: number }>,\n): Diagnostic =>\n createDiagnostic({\n ruleId: `compat/${entry.packageName}`,\n title: `${entry.packageName} compatibility note`,\n level: entry.severity,\n message: entry.reason,\n filePath: manifest.packageJsonPath,\n line: findDependencyLine(manifest.manifestContent, entry.packageName),\n sources: entry.sources,\n packageName: entry.packageName,\n replacement: entry.replacement,\n help: buildCompatHelp(entry, needsTrust),\n alsoIn,\n });\n\nconst getCompilerOptions = (project: ProjectInfo): Record<string, unknown> => {\n const compilerOptions = project.tsconfig?.compilerOptions;\n return isPlainObject(compilerOptions) ? compilerOptions : {};\n};\n\nconst usesBunGlobal = (project: ProjectInfo): boolean =>\n project.sourceFiles.some((sourceFile) => /\\bBun\\.|from\\s+[\"']bun[\"']|require\\([\"']bun[\"']\\)/.test(sourceFile.content));\n\nconst hasDependency = (project: ProjectInfo, packageName: string): boolean =>\n Boolean(project.dependencies[packageName]);\n\nconst findDependencyManifests = (project: ProjectInfo, packageName: string): PackageManifest[] =>\n project.packageManifests.filter((manifest) => Boolean(manifest.dependencies[packageName]));\n\nconst hasPackageJsonWorkspaces = (project: ProjectInfo): boolean => Boolean(project.packageJson.workspaces);\n\nconst hasCatalogReference = (project: ProjectInfo): boolean =>\n Object.values(project.dependencies).some((version) => version.startsWith(\"catalog:\"));\n\nconst hasPackageJsonCatalog = (project: ProjectInfo): boolean =>\n Boolean(project.packageJson.catalog) ||\n Boolean(project.packageJson.catalogs) ||\n (isPlainObject(project.packageJson.workspaces) &&\n (Boolean(project.packageJson.workspaces.catalog) || Boolean(project.packageJson.workspaces.catalogs)));\n\nconst workflowUsesBun = (content: string): boolean => /\\bbun\\s+(install|run|test|build|x)\\b/.test(content);\nconst workflowUsesSetupBun = (content: string): boolean => /oven-sh\\/setup-bun@/.test(content);\nconst workflowUsesLegacyInstall = (content: string): boolean =>\n /\\b(npm ci|npm install|pnpm install|yarn install|yarn --frozen-lockfile)\\b/.test(content);\nconst workflowUsesUnfrozenBunInstall = (content: string): boolean =>\n /^\\s*(-\\s*)?run:\\s*bun install\\s*$/m.test(content) || /\\bbun install\\b(?![^\\n]*--frozen-lockfile)/.test(content);\n\nexport const runPackageRules = (project: ProjectInfo): Diagnostic[] => {\n const diagnostics: Diagnostic[] = [];\n const packageJsonPath = project.packageJsonPath;\n const rootManifestContent = project.packageManifests[0]?.manifestContent ?? \"\";\n const hasBunLock = project.lockfiles.includes(\"bun.lock\");\n const hasBunLockb = project.lockfiles.includes(\"bun.lockb\");\n const hasAnyBunLock = hasBunLock || hasBunLockb;\n\n if (!hasAnyBunLock) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/lockfile-missing\",\n title: \"Missing Bun lockfile\",\n level: \"migration\",\n message: \"This project has no bun.lock, so installs and Bun CI cannot be treated as reproducible.\",\n filePath: packageJsonPath,\n sources: [BUN_DOCS.lockfile],\n help: \"Run bun install and commit bun.lock.\",\n }),\n );\n }\n\n if (hasBunLockb) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/legacy-lockb\",\n title: \"Legacy binary Bun lockfile\",\n level: \"migration\",\n message: \"bun.lockb is the legacy binary lockfile format, superseded by the text-based bun.lock in Bun v1.2+.\",\n filePath: path.join(project.rootDirectory, \"bun.lockb\"),\n sources: [BUN_DOCS.lockfile],\n help: \"Migrate with bun install --save-text-lockfile --frozen-lockfile --lockfile-only, then remove bun.lockb after verification.\",\n }),\n );\n }\n\n if (hasAnyBunLock && project.legacyLockfiles.length > 0) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/mixed-lockfiles\",\n title: \"Mixed package-manager lockfiles\",\n level: \"risk\",\n message: `Bun lockfile coexists with ${project.legacyLockfiles.join(\", \")}, leaving dependency resolution ownership ambiguous.`,\n filePath: packageJsonPath,\n sources: [BUN_DOCS.lockfile],\n help: \"Keep legacy lockfiles only if another supported workflow still owns them; otherwise remove them after validating bun.lock.\",\n }),\n );\n }\n\n if (!project.packageJson.packageManager?.startsWith(\"bun@\")) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/package-manager-field\",\n title: \"packageManager does not pin Bun\",\n level: \"migration\",\n message: \"package.json does not pin Bun in packageManager, so contributors and CI may use different package managers.\",\n filePath: packageJsonPath,\n line: project.packageJson.packageManager ? findKeyLine(rootManifestContent, \"packageManager\") : 1,\n sources: [BUN_DOCS.lockfile],\n help: \"Set packageManager to the Bun version used by the project, for example bun@1.3.11.\",\n }),\n );\n }\n\n if (project.pnpmWorkspacePath && !hasPackageJsonWorkspaces(project)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/pnpm-workspace-only\",\n title: \"Workspaces only declared for pnpm\",\n level: \"blocker\",\n message: \"Bun reads workspaces from package.json, so a pnpm-workspace.yaml without a matching package.json workspaces entry will not define Bun workspaces.\",\n filePath: project.pnpmWorkspacePath,\n line: findYamlKeyLine(project.pnpmWorkspaceContent, \"packages\"),\n sources: [BUN_DOCS.workspaces],\n help: \"Move workspace globs into package.json workspaces before relying on bun install at the repo root.\",\n }),\n );\n }\n\n if (hasCatalogReference(project) && !hasPackageJsonCatalog(project)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/catalog-without-package-json-catalog\",\n title: \"Catalog references need Bun catalog definitions\",\n level: \"blocker\",\n message: \"This project uses catalog: dependency references, but no Bun catalog or catalogs definition was found in package.json.\",\n filePath: packageJsonPath,\n line: findCatalogReferenceLine(rootManifestContent),\n sources: [BUN_DOCS.catalogs],\n help: \"Define catalog or catalogs in package.json, preferably under workspaces for monorepos.\",\n }),\n );\n }\n\n for (const workflow of project.workflows) {\n if (workflowUsesBun(workflow.content) && !workflowUsesSetupBun(workflow.content)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/ci-missing-setup-bun\",\n title: \"CI uses Bun without setup-bun\",\n level: \"blocker\",\n message: \"This workflow runs bun commands but does not install Bun with oven-sh/setup-bun.\",\n filePath: workflow.filePath,\n line: findLineNumber(workflow.content, /\\bbun\\s+(install|run|test|build|x)\\b/),\n sources: [BUN_DOCS.ci],\n help: \"Add oven-sh/setup-bun before bun commands in GitHub Actions.\",\n }),\n );\n }\n\n if (hasAnyBunLock && workflowUsesLegacyInstall(workflow.content)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/ci-uses-legacy-package-manager\",\n title: \"CI still installs with another package manager\",\n level: \"migration\",\n message: \"This workflow uses npm, pnpm, or yarn install even though the project has a Bun lockfile.\",\n filePath: workflow.filePath,\n line: findLineNumber(workflow.content, /\\b(npm ci|npm install|pnpm install|yarn install|yarn --frozen-lockfile)\\b/),\n sources: [BUN_DOCS.ci],\n help: \"Switch Bun-owned CI jobs to bun install --frozen-lockfile.\",\n }),\n );\n }\n\n if (workflowUsesUnfrozenBunInstall(workflow.content)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/ci-install-not-frozen\",\n title: \"CI Bun install is not frozen\",\n level: \"risk\",\n message: \"This workflow runs bun install without --frozen-lockfile, so CI can update bun.lock instead of verifying it.\",\n filePath: workflow.filePath,\n line: findLineNumber(workflow.content, /\\bbun install\\b/),\n sources: [BUN_DOCS.bunfig, BUN_DOCS.ci],\n help: \"Use bun install --frozen-lockfile in CI.\",\n }),\n );\n }\n }\n\n if (usesBunGlobal(project) && !hasDependency(project, \"@types/bun\")) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/types-package-missing\",\n title: \"Bun types are missing\",\n level: \"migration\",\n message: \"Source files reference Bun APIs, but @types/bun is not installed.\",\n filePath: packageJsonPath,\n sources: [BUN_DOCS.typescript],\n help: \"Install @types/bun as a dev dependency.\",\n }),\n );\n }\n\n const compilerOptions = getCompilerOptions(project);\n const compilerTypes = compilerOptions.types;\n if (hasDependency(project, \"@types/bun\") && Array.isArray(compilerTypes) && !compilerTypes.includes(\"bun\")) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/tsconfig-types-missing-bun\",\n title: \"tsconfig types excludes Bun\",\n level: \"risk\",\n message: \"@types/bun is installed, but compilerOptions.types does not include bun. TypeScript 6+ requires explicit Bun types in this mode.\",\n filePath: project.tsconfigPath ?? packageJsonPath,\n line: findKeyLine(project.tsconfigContent, \"types\"),\n sources: [BUN_DOCS.typescript],\n help: \"Add \\\"bun\\\" to compilerOptions.types or remove types if you do not need to restrict global type packages.\",\n }),\n );\n }\n\n if (project.bunfig?.installAuto && project.bunfig.installAuto !== \"disable\") {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/auto-install-enabled\",\n title: \"Bun auto-install is enabled\",\n level: \"risk\",\n message: `bunfig.toml sets install.auto to ${project.bunfig.installAuto}, letting Bun fetch packages during execution when node_modules is absent.`,\n filePath: project.bunfig.filePath,\n line: findLineNumber(project.bunfig.content, /auto\\s*=/),\n sources: [BUN_DOCS.autoInstall, BUN_DOCS.bunfig],\n help: \"For application repos and CI, consider install.auto = \\\"disable\\\" for more predictable execution.\",\n }),\n );\n }\n\n for (const entry of COMPAT_DB) {\n const matchingManifests = findDependencyManifests(project, entry.packageName);\n if (matchingManifests.length === 0) continue;\n\n if (entry.severity === \"win\") {\n const [primaryManifest, ...otherManifests] = matchingManifests;\n if (!primaryManifest) continue;\n const isTrustedEverywhere = matchingManifests.every((manifest) =>\n manifest.trustedDependencies.has(entry.packageName),\n );\n const needsTrust = Boolean(entry.requiresTrustedDependency) && !isTrustedEverywhere;\n const alsoIn = otherManifests.map((manifest) => ({\n filePath: manifest.packageJsonPath,\n line: findDependencyLine(manifest.manifestContent, entry.packageName),\n }));\n diagnostics.push(createCompatDiagnostic(entry, primaryManifest, needsTrust, alsoIn));\n continue;\n }\n\n for (const manifest of matchingManifests) {\n const needsTrust =\n Boolean(entry.requiresTrustedDependency) && !manifest.trustedDependencies.has(entry.packageName);\n diagnostics.push(createCompatDiagnostic(entry, manifest, needsTrust));\n }\n }\n\n return diagnostics;\n};\n\nconst CODE_RULES: CodeRule[] = [\n {\n ruleId: \"code/node-repl\",\n title: \"node:repl is not implemented in Bun\",\n level: \"blocker\",\n pattern: /(?:from\\s+[\"']node:repl[\"']|require\\([\"']node:repl[\"']\\))/,\n message: \"Bun's Node compatibility table marks node:repl as not implemented.\",\n sources: [BUN_DOCS.nodeCompatibility],\n },\n {\n ruleId: \"code/node-trace-events\",\n title: \"node:trace_events is not implemented in Bun\",\n level: \"blocker\",\n pattern: /(?:from\\s+[\"']node:trace_events[\"']|require\\([\"']node:trace_events[\"']\\))/,\n message: \"Bun's Node compatibility table marks node:trace_events as not implemented.\",\n sources: [BUN_DOCS.nodeCompatibility],\n },\n {\n ruleId: \"code/node-sqlite\",\n title: \"node:sqlite is not implemented in Bun\",\n level: \"blocker\",\n pattern: /(?:from\\s+[\"']node:sqlite[\"']|require\\([\"']node:sqlite[\"']\\))/,\n message: \"Bun does not implement node:sqlite. Bun provides bun:sqlite instead.\",\n sources: [BUN_DOCS.nodeCompatibility, BUN_DOCS.sqlite],\n help: \"Use bun:sqlite for Bun-targeted SQLite code.\",\n },\n {\n ruleId: \"code/process-binding\",\n title: \"process.binding usage is compatibility-sensitive\",\n level: \"risk\",\n pattern: /\\bprocess\\.binding\\s*\\(/,\n message: \"process.binding is an internal Node API and only partially implemented by Bun.\",\n sources: [BUN_DOCS.nodeCompatibility],\n help: \"Replace internal Node binding access with public APIs before migrating.\",\n },\n {\n ruleId: \"code/process-missing-api\",\n title: \"Node process API is not implemented in Bun\",\n level: \"blocker\",\n pattern: /\\bprocess\\.(loadEnvFile|getBuiltinModule)\\s*\\(/,\n message: \"Bun marks process.loadEnvFile and process.getBuiltinModule as not implemented in its Node compatibility docs.\",\n sources: [BUN_DOCS.nodeCompatibility],\n },\n {\n ruleId: \"code/module-register\",\n title: \"module.register is not implemented in Bun\",\n level: \"blocker\",\n pattern: /\\bmodule\\.register\\s*\\(/,\n message: \"Bun lists module.register as not implemented and recommends Bun.plugin instead.\",\n sources: [BUN_DOCS.nodeCompatibility, BUN_DOCS.plugins],\n help: \"Evaluate Bun.plugin or avoid runtime module loader hooks in Bun-targeted code.\",\n },\n {\n ruleId: \"code/node-test\",\n title: \"node:test is only partly implemented in Bun\",\n level: \"migration\",\n pattern: /(?:from\\s+[\"']node:test[\"']|require\\([\"']node:test[\"']\\))/,\n message: \"Bun marks node:test as partly implemented and provides bun:test as its native runner.\",\n sources: [BUN_DOCS.nodeCompatibility, BUN_DOCS.testRunner],\n help: \"Prefer bun:test when migrating the test runner to Bun.\",\n },\n {\n ruleId: \"code/v8-specific-api\",\n title: \"V8-specific APIs are compatibility-sensitive\",\n level: \"risk\",\n pattern: /(?:from\\s+[\"']node:v8[\"']|require\\([\"']node:v8[\"']\\)|\\bv8\\.(serialize|deserialize|setFlagsFromString|cachedDataVersionTag)\\s*\\()/,\n message: \"Bun runs on JavaScriptCore, and its Node v8 compatibility is partial.\",\n sources: [BUN_DOCS.nodeCompatibility],\n help: \"Avoid V8-specific runtime assumptions in Bun-targeted code.\",\n },\n {\n ruleId: \"code/worker-resource-limits\",\n title: \"worker_threads resource limits are unsupported\",\n level: \"risk\",\n pattern: /\\bresourceLimits\\s*:/,\n requiresPattern: /(?:from\\s+[\"']node:worker_threads[\"']|require\\([\"']node:worker_threads[\"']\\)|from\\s+[\"']worker_threads[\"']|require\\([\"']worker_threads[\"']\\)|\\bnew\\s+Worker\\s*\\()/,\n message: \"Bun marks worker_threads resourceLimits as unsupported in its Node compatibility notes.\",\n sources: [BUN_DOCS.nodeCompatibility],\n help: \"Verify worker behavior under Bun or avoid Node-specific Worker options.\",\n },\n];\n\nexport const runCodeRules = (project: ProjectInfo): Diagnostic[] => {\n const diagnostics: Diagnostic[] = [];\n\n for (const sourceFile of project.sourceFiles) {\n for (const rule of CODE_RULES) {\n if (rule.requiresPattern && !rule.requiresPattern.test(sourceFile.content)) continue;\n if (!rule.pattern.test(sourceFile.content)) continue;\n diagnostics.push(\n createDiagnostic({\n ruleId: rule.ruleId,\n title: rule.title,\n level: rule.level,\n message: rule.message,\n filePath: sourceFile.filePath,\n line: findLineNumber(sourceFile.content, rule.pattern),\n sources: rule.sources,\n help: rule.help,\n }),\n );\n }\n }\n\n return diagnostics;\n};\n","import {\n BLOCKER_RULE_PENALTY,\n MIGRATION_RULE_PENALTY,\n PERFECT_SCORE,\n RISK_RULE_PENALTY,\n SCORE_CLOSE_THRESHOLD,\n SCORE_READY_THRESHOLD,\n SCORE_RISKY_THRESHOLD,\n} from \"./constants.js\";\nimport type { Diagnostic, FindingLevel, ScanSummary, ScoreResult } from \"./types.js\";\n\nconst getScoreLabel = (score: number): ScoreResult[\"label\"] => {\n if (score >= SCORE_READY_THRESHOLD) return \"Ready\";\n if (score >= SCORE_CLOSE_THRESHOLD) return \"Close\";\n if (score >= SCORE_RISKY_THRESHOLD) return \"Risky\";\n return \"Blocked\";\n};\n\nconst collectUniqueRuleCounts = (diagnostics: Diagnostic[]): Record<FindingLevel, Set<string>> => {\n const counts: Record<FindingLevel, Set<string>> = {\n blocker: new Set(),\n risk: new Set(),\n migration: new Set(),\n win: new Set(),\n };\n\n for (const diagnostic of diagnostics) {\n counts[diagnostic.level].add(diagnostic.ruleId);\n }\n\n return counts;\n};\n\nexport const calculateScore = (diagnostics: Diagnostic[]): ScoreResult => {\n const counts = collectUniqueRuleCounts(diagnostics);\n const penalty =\n counts.blocker.size * BLOCKER_RULE_PENALTY +\n counts.risk.size * RISK_RULE_PENALTY +\n counts.migration.size * MIGRATION_RULE_PENALTY;\n const score = Math.max(0, Math.round(PERFECT_SCORE - penalty));\n return { score, label: getScoreLabel(score) };\n};\n\nexport const summarizeDiagnostics = (diagnostics: Diagnostic[]): ScanSummary => ({\n blockers: diagnostics.filter((diagnostic) => diagnostic.level === \"blocker\").length,\n risks: diagnostics.filter((diagnostic) => diagnostic.level === \"risk\").length,\n migrations: diagnostics.filter((diagnostic) => diagnostic.level === \"migration\").length,\n wins: diagnostics.filter((diagnostic) => diagnostic.level === \"win\").length,\n});\n","import { loadConfig, filterIgnoredDiagnostics } from \"./config.js\";\nimport { discoverProject } from \"./project.js\";\nimport { runCodeRules, runPackageRules } from \"./rules.js\";\nimport { calculateScore, summarizeDiagnostics } from \"./score.js\";\nimport type { Diagnostic, ScanOptions, ScanResult } from \"./types.js\";\n\nconst collapseDuplicateRules = (diagnostics: Diagnostic[]): Diagnostic[] => {\n const seenByRuleId = new Map<string, Diagnostic>();\n for (const diagnostic of diagnostics) {\n const existing = seenByRuleId.get(diagnostic.ruleId);\n if (!existing) {\n seenByRuleId.set(diagnostic.ruleId, diagnostic);\n continue;\n }\n seenByRuleId.set(diagnostic.ruleId, {\n ...existing,\n alsoIn: [...(existing.alsoIn ?? []), { filePath: diagnostic.filePath, line: diagnostic.line }],\n });\n }\n return [...seenByRuleId.values()];\n};\n\nexport const scan = async (directory: string, options: ScanOptions = {}): Promise<ScanResult> => {\n const project = discoverProject(directory);\n const loadedConfig = options.configOverride ?? loadConfig(project.rootDirectory, project.packageJson);\n const shouldRunPackageChecks = options.packageChecks ?? loadedConfig.package ?? true;\n const shouldRunCodeChecks = options.codeChecks ?? loadedConfig.code ?? true;\n\n const rawDiagnostics = [\n ...(shouldRunPackageChecks ? runPackageRules(project) : []),\n ...(shouldRunCodeChecks ? runCodeRules(project) : []),\n ];\n const collapsedDiagnostics = collapseDuplicateRules(rawDiagnostics);\n const filteredDiagnostics = filterIgnoredDiagnostics(\n collapsedDiagnostics,\n loadedConfig,\n project.rootDirectory,\n );\n\n return {\n project,\n diagnostics: filteredDiagnostics,\n score: calculateScore(filteredDiagnostics),\n summary: summarizeDiagnostics(filteredDiagnostics),\n };\n};\n"],"mappings":"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CF,SAAS,CAAC,SAAS,OAAO,SAAS,kBAAkB;EACrD,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,UAAU;EAC7B,cAAc;EACd,YACE;EACF,2BAA2B;EAC5B;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,qBAAqB;EACxC,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,SAAS,SAAS,eAAe;EACpD,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,OAAO,SAAS,kBAAkB;EACrD,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,MAAM;EACzB,cAAc;EACd,aAAa;EACb,YACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,YAAY,SAAS,kBAAkB;EAC1D,cAAc;EACd,aAAa;EACb,YACE;EACF,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,WAAW;EAC9B,cAAc;EACd,aAAa;EACb,YACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,WAAW,SAAS,kBAAkB;EACzD,cAAc;EACd,YACE;EACF,2BAA2B;EAC5B;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,OAAO,SAAS,kBAAkB;EACrD,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,UAAU;EAC7B,cAAc;EACd,YACE;EACF,2BA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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-doctor",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Diagnose Node-to-Bun migration readiness for JavaScript and TypeScript projects",
5
5
  "keywords": [
6
6
  "bun",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: bun-doctor
3
3
  description: 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.
4
- version: "1.0.0"
4
+ version: "0.0.4"
5
5
  ---
6
6
 
7
7
  # Bun Doctor
@@ -1 +0,0 @@
1
- {"version":3,"file":"scan-BtfKcvQl.mjs","names":["parseToml"],"sources":["../src/bun-docs.ts","../src/constants.ts","../src/utils.ts","../src/config.ts","../src/project.ts","../src/compat-db.ts","../src/rules.ts","../src/score.ts","../src/scan.ts"],"sourcesContent":["// Generated by scripts/refresh-bun-docs.ts.\n// Do not edit by hand — run `bun run docs:refresh` to update.\n\nexport const BUN_DOCS = {\n autoInstall: \"https://bun.com/docs/runtime/auto-install\",\n bundler: \"https://bun.com/docs/bundler\",\n bundlerEsbuild: \"https://bun.com/docs/bundler/esbuild\",\n bunfig: \"https://bun.com/docs/runtime/bunfig\",\n catalogs: \"https://bun.com/docs/pm/catalogs\",\n ci: \"https://bun.com/docs/guides/install/cicd\",\n environmentVariables: \"https://bun.com/docs/runtime/environment-variables\",\n fetch: \"https://bun.com/docs/runtime/networking/fetch\",\n hashing: \"https://bun.com/docs/runtime/hashing\",\n lifecycle: \"https://bun.com/docs/pm/lifecycle\",\n lockfile: \"https://bun.com/docs/pm/lockfile\",\n nodeCompatibility: \"https://bun.com/docs/runtime/nodejs-compat\",\n plugins: \"https://bun.com/docs/runtime/plugins\",\n redis: \"https://bun.com/docs/runtime/redis\",\n securityScanner: \"https://bun.com/docs/pm/security-scanner-api\",\n sqlite: \"https://bun.com/docs/runtime/sqlite\",\n testConfiguration: \"https://bun.com/docs/test/configuration\",\n testRunner: \"https://bun.com/docs/test\",\n typescript: \"https://bun.com/docs/typescript\",\n watchMode: \"https://bun.com/docs/runtime/watch-mode\",\n workspaces: \"https://bun.com/docs/pm/workspaces\",\n};\n","export const VERSION = process.env.VERSION ?? \"0.0.0\";\n\nexport const PERFECT_SCORE = 100;\nexport const BLOCKER_RULE_PENALTY = 12;\nexport const RISK_RULE_PENALTY = 5;\nexport const MIGRATION_RULE_PENALTY = 2;\n\nexport const SCORE_READY_THRESHOLD = 90;\nexport const SCORE_CLOSE_THRESHOLD = 75;\nexport const SCORE_RISKY_THRESHOLD = 50;\n\nexport { BUN_DOCS } from \"./bun-docs.js\";\n\nexport const IGNORED_DIRECTORIES = new Set([\n \".git\",\n \".next\",\n \".turbo\",\n \"build\",\n \"coverage\",\n \"dist\",\n \"node_modules\",\n \"vendor\",\n]);\n\nexport const SOURCE_FILE_EXTENSIONS = new Set([\n \".cjs\",\n \".cts\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".mts\",\n \".ts\",\n \".tsx\",\n]);\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { IGNORED_DIRECTORIES, SOURCE_FILE_EXTENSIONS } from \"./constants.js\";\n\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport const fileExists = (filePath: string): boolean => {\n try {\n return fs.statSync(filePath).isFile();\n } catch {\n return false;\n }\n};\n\nexport const directoryExists = (directoryPath: string): boolean => {\n try {\n return fs.statSync(directoryPath).isDirectory();\n } catch {\n return false;\n }\n};\n\nexport const readJsonFile = <T>(filePath: string): T | null => {\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf8\")) as T;\n } catch {\n return null;\n }\n};\n\nexport const collectFiles = (rootDirectory: string, predicate: (filePath: string) => boolean): string[] => {\n const files: string[] = [];\n const stack = [rootDirectory];\n\n while (stack.length > 0) {\n const currentDirectory = stack.pop();\n if (!currentDirectory) continue;\n\n let entries: fs.Dirent[] = [];\n try {\n entries = fs.readdirSync(currentDirectory, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n const entryPath = path.join(currentDirectory, entry.name);\n if (entry.isDirectory()) {\n if (!entry.name.startsWith(\".\") && !IGNORED_DIRECTORIES.has(entry.name)) {\n stack.push(entryPath);\n }\n continue;\n }\n if (entry.isFile() && predicate(entryPath)) {\n files.push(entryPath);\n }\n }\n }\n\n return files.sort();\n};\n\nexport const isSourceFilePath = (filePath: string): boolean => {\n if (filePath.endsWith(\".d.ts\")) return false;\n return SOURCE_FILE_EXTENSIONS.has(path.extname(filePath));\n};\n\nexport const toRelativePath = (filePath: string, rootDirectory: string): string =>\n path.relative(rootDirectory, filePath).replaceAll(path.sep, \"/\") || \".\";\n\nexport const findLineNumber = (content: string, pattern: RegExp): number => {\n const lines = content.split(/\\r?\\n/);\n const flags = pattern.flags.replace(\"g\", \"\");\n const linePattern = new RegExp(pattern.source, flags);\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n if (linePattern.test(lines[lineIndex] ?? \"\")) return lineIndex + 1;\n }\n\n return 1;\n};\n\nexport const escapeRegExp = (text: string): string => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\nexport const wildcardToRegExp = (pattern: string): RegExp => {\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replaceAll(\"**\", \"__DOUBLE_STAR__\")\n .replaceAll(\"*\", \"[^/]*\")\n .replaceAll(\"__DOUBLE_STAR__\", \".*\");\n return new RegExp(`^${escaped}$`);\n};\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { BunDoctorConfig, Diagnostic, PackageJson } from \"./types.js\";\nimport { readJsonFile, toRelativePath, wildcardToRegExp } from \"./utils.js\";\n\nconst CONFIG_FILE_NAME = \"bun-doctor.config.json\";\n\nexport const loadConfig = (rootDirectory: string, packageJson: PackageJson): BunDoctorConfig => {\n const configPath = path.join(rootDirectory, CONFIG_FILE_NAME);\n if (fs.existsSync(configPath)) {\n return readJsonFile<BunDoctorConfig>(configPath) ?? {};\n }\n return packageJson.bunDoctor ?? {};\n};\n\nexport const filterIgnoredDiagnostics = (\n diagnostics: Diagnostic[],\n config: BunDoctorConfig,\n rootDirectory: string,\n): Diagnostic[] => {\n const ignoredRules = new Set(config.ignore?.rules ?? []);\n const ignoredFilePatterns = (config.ignore?.files ?? []).map(wildcardToRegExp);\n\n return diagnostics.filter((diagnostic) => {\n if (ignoredRules.has(diagnostic.ruleId)) return false;\n const relativePath = toRelativePath(diagnostic.filePath, rootDirectory);\n return !ignoredFilePatterns.some((pattern) => pattern.test(relativePath));\n });\n};\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport type {\n BunfigInfo,\n PackageJson,\n PackageManifest,\n ProjectInfo,\n SourceFile,\n WorkflowFile,\n} from \"./types.js\";\nimport { collectFiles, fileExists, isPlainObject, isSourceFilePath, readJsonFile, wildcardToRegExp } from \"./utils.js\";\n\nconst LOCKFILE_NAMES = [\"bun.lock\", \"bun.lockb\"];\nconst LEGACY_LOCKFILE_NAMES = [\"package-lock.json\", \"pnpm-lock.yaml\", \"yarn.lock\"];\nconst WORKFLOW_EXTENSIONS = new Set([\".yml\", \".yaml\"]);\n\nconst collectDependencies = (packageJson: PackageJson): Record<string, string> => ({\n ...packageJson.optionalDependencies,\n ...packageJson.peerDependencies,\n ...packageJson.devDependencies,\n ...packageJson.dependencies,\n});\n\nconst readManifestContent = (packageJsonPath: string): string => {\n try {\n return fs.readFileSync(packageJsonPath, \"utf8\");\n } catch {\n return \"\";\n }\n};\n\nconst toPackageManifest = (packageJsonPath: string, packageJson: PackageJson): PackageManifest => ({\n packageJsonPath,\n packageJson,\n packageName: packageJson.name ?? path.basename(path.dirname(packageJsonPath)),\n dependencies: collectDependencies(packageJson),\n trustedDependencies: new Set(packageJson.trustedDependencies ?? []),\n manifestContent: readManifestContent(packageJsonPath),\n});\n\nconst extractPackageJsonWorkspaceGlobs = (packageJson: PackageJson): string[] => {\n const workspaces = packageJson.workspaces;\n if (Array.isArray(workspaces)) return workspaces;\n if (isPlainObject(workspaces) && Array.isArray(workspaces.packages)) return workspaces.packages as string[];\n return [];\n};\n\nconst parsePnpmWorkspaceGlobs = (content: string): string[] => {\n const globs: string[] = [];\n let inPackagesBlock = false;\n let packagesIndent = -1;\n for (const rawLine of content.split(/\\r?\\n/)) {\n if (/^\\s*#/.test(rawLine)) continue;\n if (rawLine.trim() === \"\") continue;\n const leadingWhitespace = rawLine.length - rawLine.trimStart().length;\n if (/^packages\\s*:\\s*$/.test(rawLine.trimEnd())) {\n inPackagesBlock = true;\n packagesIndent = leadingWhitespace;\n continue;\n }\n if (!inPackagesBlock) continue;\n if (leadingWhitespace <= packagesIndent) {\n inPackagesBlock = false;\n continue;\n }\n const itemMatch = rawLine.match(/^\\s*-\\s*[\"']?([^\"']+?)[\"']?\\s*$/);\n if (itemMatch?.[1]) globs.push(itemMatch[1]);\n }\n return globs;\n};\n\ninterface SplitGlobs {\n positive: string[];\n negative: string[];\n}\n\nconst splitGlobs = (globs: string[]): SplitGlobs => {\n const positive: string[] = [];\n const negative: string[] = [];\n for (const glob of globs) {\n if (glob.startsWith(\"!\")) negative.push(glob.slice(1));\n else positive.push(glob);\n }\n return { positive, negative };\n};\n\nconst pathMatchesAnyGlob = (relativePath: string, globs: string[]): boolean => {\n for (const glob of globs) {\n if (wildcardToRegExp(glob).test(relativePath)) return true;\n }\n return false;\n};\n\nconst isWorkspacePath = (relativePath: string, splitWorkspaceGlobs: SplitGlobs): boolean =>\n pathMatchesAnyGlob(relativePath, splitWorkspaceGlobs.positive) &&\n !pathMatchesAnyGlob(relativePath, splitWorkspaceGlobs.negative);\n\nconst collectPackageManifests = (\n rootDirectory: string,\n rootPackageJsonPath: string,\n rootPackageJson: PackageJson,\n workspaceGlobs: string[],\n): PackageManifest[] => {\n const rootManifest = toPackageManifest(rootPackageJsonPath, rootPackageJson);\n if (workspaceGlobs.length === 0) return [rootManifest];\n\n const splitWorkspaceGlobs = splitGlobs(workspaceGlobs);\n if (splitWorkspaceGlobs.positive.length === 0) return [rootManifest];\n\n const manifestPaths = collectFiles(\n rootDirectory,\n (filePath) => path.basename(filePath) === \"package.json\" && filePath !== rootPackageJsonPath,\n );\n const workspaceManifests = manifestPaths.flatMap((manifestPath) => {\n const relativeDirectory = path\n .relative(rootDirectory, path.dirname(manifestPath))\n .replaceAll(path.sep, \"/\");\n if (!isWorkspacePath(relativeDirectory, splitWorkspaceGlobs)) return [];\n const packageJson = readJsonFile<PackageJson>(manifestPath);\n if (!packageJson || typeof packageJson !== \"object\" || Array.isArray(packageJson)) return [];\n return [toPackageManifest(manifestPath, packageJson)];\n });\n return [rootManifest, ...workspaceManifests];\n};\n\nconst mergeDependencies = (manifests: PackageManifest[]): Record<string, string> => {\n const dependencies: Record<string, string> = {};\n for (const manifest of manifests) {\n Object.assign(dependencies, manifest.dependencies);\n }\n return dependencies;\n};\n\nconst mergeTrustedDependencies = (manifests: PackageManifest[]): Set<string> => {\n const trustedDependencies = new Set<string>();\n for (const manifest of manifests) {\n for (const packageName of manifest.trustedDependencies) {\n trustedDependencies.add(packageName);\n }\n }\n return trustedDependencies;\n};\n\nconst getTomlSection = (parsed: Record<string, unknown>, sectionPath: string[]): Record<string, unknown> | null => {\n let current: unknown = parsed;\n for (const segment of sectionPath) {\n if (!isPlainObject(current)) return null;\n current = (current as Record<string, unknown>)[segment];\n }\n return isPlainObject(current) ? (current as Record<string, unknown>) : null;\n};\n\nconst readBooleanField = (section: Record<string, unknown> | null, key: string): boolean | undefined => {\n const value = section?.[key];\n return typeof value === \"boolean\" ? value : undefined;\n};\n\nconst readStringField = (section: Record<string, unknown> | null, key: string): string | undefined => {\n const value = section?.[key];\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst parseBunfig = (rootDirectory: string): BunfigInfo | null => {\n const filePath = path.join(rootDirectory, \"bunfig.toml\");\n if (!fileExists(filePath)) return null;\n const content = fs.readFileSync(filePath, \"utf8\");\n\n let parsed: Record<string, unknown>;\n try {\n parsed = parseToml(content) as Record<string, unknown>;\n } catch {\n return { filePath, content };\n }\n\n const installSection = getTomlSection(parsed, [\"install\"]);\n const securitySection = getTomlSection(parsed, [\"install\", \"security\"]);\n\n return {\n filePath,\n content,\n installIgnoreScripts: readBooleanField(installSection, \"ignoreScripts\"),\n installFrozenLockfile: readBooleanField(installSection, \"frozenLockfile\"),\n installAuto: readStringField(installSection, \"auto\"),\n installSecurityScanner: readStringField(securitySection, \"scanner\"),\n };\n};\n\nconst readSourceFiles = (rootDirectory: string): SourceFile[] =>\n collectFiles(rootDirectory, isSourceFilePath).map((filePath) => ({\n filePath,\n content: fs.readFileSync(filePath, \"utf8\"),\n }));\n\nconst readWorkflowFiles = (rootDirectory: string): WorkflowFile[] => {\n const workflowDirectory = path.join(rootDirectory, \".github\", \"workflows\");\n if (!fs.existsSync(workflowDirectory)) return [];\n return collectFiles(workflowDirectory, (filePath) => WORKFLOW_EXTENSIONS.has(path.extname(filePath))).map(\n (filePath) => ({ filePath, content: fs.readFileSync(filePath, \"utf8\") }),\n );\n};\n\nconst readTsconfig = (\n rootDirectory: string,\n): { path: string | null; config: Record<string, unknown> | null; content: string | null } => {\n const tsconfigPath = path.join(rootDirectory, \"tsconfig.json\");\n if (!fileExists(tsconfigPath)) return { path: null, config: null, content: null };\n const config = readJsonFile<Record<string, unknown>>(tsconfigPath);\n let content: string | null = null;\n try {\n content = fs.readFileSync(tsconfigPath, \"utf8\");\n } catch {\n content = null;\n }\n return { path: tsconfigPath, config, content };\n};\n\nconst findPackageJsonPath = (startDirectory: string): string => {\n const packageJsonPath = path.join(startDirectory, \"package.json\");\n if (fileExists(packageJsonPath)) return packageJsonPath;\n throw new Error(`No package.json found in ${startDirectory}`);\n};\n\nexport const discoverProject = (directory: string): ProjectInfo => {\n const rootDirectory = path.resolve(directory);\n const packageJsonPath = findPackageJsonPath(rootDirectory);\n const packageJson = readJsonFile<PackageJson>(packageJsonPath);\n if (!packageJson || typeof packageJson !== \"object\" || Array.isArray(packageJson)) {\n throw new Error(`Could not parse ${packageJsonPath}`);\n }\n\n const lockfiles = LOCKFILE_NAMES.filter((lockfileName) => fileExists(path.join(rootDirectory, lockfileName)));\n const legacyLockfiles = LEGACY_LOCKFILE_NAMES.filter((lockfileName) =>\n fileExists(path.join(rootDirectory, lockfileName)),\n );\n const pnpmWorkspacePath = path.join(rootDirectory, \"pnpm-workspace.yaml\");\n const pnpmWorkspaceContent = fileExists(pnpmWorkspacePath) ? fs.readFileSync(pnpmWorkspacePath, \"utf8\") : null;\n const workspaceGlobs = [\n ...extractPackageJsonWorkspaceGlobs(packageJson),\n ...(pnpmWorkspaceContent ? parsePnpmWorkspaceGlobs(pnpmWorkspaceContent) : []),\n ];\n const packageManifests = collectPackageManifests(rootDirectory, packageJsonPath, packageJson, workspaceGlobs);\n const tsconfig = readTsconfig(rootDirectory);\n\n return {\n rootDirectory,\n packageJsonPath,\n packageJson,\n packageName: packageJson.name ?? path.basename(rootDirectory),\n dependencies: mergeDependencies(packageManifests),\n trustedDependencies: mergeTrustedDependencies(packageManifests),\n packageManifests,\n lockfiles,\n legacyLockfiles,\n bunfig: parseBunfig(rootDirectory),\n tsconfigPath: tsconfig.path,\n tsconfig: tsconfig.config,\n tsconfigContent: tsconfig.content,\n workflows: readWorkflowFiles(rootDirectory),\n sourceFiles: readSourceFiles(rootDirectory),\n pnpmWorkspacePath: pnpmWorkspaceContent ? pnpmWorkspacePath : null,\n pnpmWorkspaceContent,\n };\n};\n","import { BUN_DOCS } from \"./constants.js\";\nimport type { CompatEntry } from \"./types.js\";\n\nconst LAST_VERIFIED = \"2026-05-12\";\n\nexport const COMPAT_DB: CompatEntry[] = [\n {\n packageName: \"@napi-rs/canvas\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"@napi-rs/canvas ships prebuilt N-API binaries that generally work on Bun without lifecycle script gymnastics, unlike node-canvas.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n migrationHint:\n \"Prefer @napi-rs/canvas over node-canvas when migrating to Bun for a cleaner install path.\",\n },\n {\n packageName: \"@prisma/client\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"Prisma Client generates code via a postinstall script that Bun skips for dependencies by default.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add @prisma/client to trustedDependencies, or run prisma generate explicitly in the build step.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"@swc-node/register\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun runs TypeScript directly, making @swc-node/register redundant when Bun is the target runtime.\",\n sources: [BUN_DOCS.typescript],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun run file.ts\",\n migrationHint: \"Remove @swc-node/register loader flags from Bun-targeted scripts.\",\n },\n {\n packageName: \"argon2\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Bun.password supports argon2 natively, and the argon2 npm package's native install depends on lifecycle scripts Bun skips by default.\",\n sources: [BUN_DOCS.hashing, BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.password\",\n migrationHint:\n \"Consider Bun.password with algorithm 'argon2id' for new Bun-only password hashing code.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"ava\",\n severity: \"win\",\n confidence: \"low\",\n reason:\n \"AVA runs on Bun, but its test paradigm differs from bun:test enough that migration is real work rather than a script swap.\",\n sources: [BUN_DOCS.testRunner],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:test\",\n workaround:\n \"Keep AVA if its assertion model or concurrency behavior matters; otherwise bun:test is the Bun-native alternative.\",\n },\n {\n packageName: \"axios\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun ships native fetch and Web HTTP APIs that cover most axios use cases, though axios continues to work on Bun.\",\n sources: [BUN_DOCS.fetch, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"global fetch\",\n migrationHint:\n \"Evaluate global fetch for simple JSON flows; keep axios where you rely on its interceptors, defaults, or error model.\",\n },\n {\n packageName: \"bcrypt\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Bun.password provides native bcrypt hashing, and the bcrypt npm package's native install requires lifecycle scripts Bun skips by default.\",\n sources: [BUN_DOCS.hashing, BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.password\",\n migrationHint:\n \"Bun.password is a drop-in replacement for most bcrypt.hash/compare flows when Bun is the target runtime.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"bcryptjs\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"bcryptjs runs fine on Bun, but Bun.password offers a native bcrypt implementation that is typically much faster.\",\n sources: [BUN_DOCS.hashing],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.password\",\n migrationHint:\n \"Bun.password covers most bcryptjs use cases with better performance when targeting Bun.\",\n },\n {\n packageName: \"better-sqlite3\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"better-sqlite3 uses native bindings and lifecycle scripts that Bun skips for dependencies by default, and Bun ships a native SQLite driver.\",\n sources: [BUN_DOCS.sqlite, BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:sqlite\",\n workaround:\n \"If you keep better-sqlite3, add it to trustedDependencies and verify install on every target platform.\",\n migrationHint:\n \"Database from bun:sqlite covers the synchronous-driver use case better-sqlite3 is usually chosen for.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"chokidar\",\n severity: \"win\",\n confidence: \"low\",\n reason:\n \"chokidar runs on Bun via fs.watch fallbacks, but Bun's --watch mode and fs.watch cover most file-watcher use cases natively.\",\n sources: [BUN_DOCS.watchMode, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Keep chokidar if you rely on its cross-platform glob semantics or stability heuristics; otherwise evaluate native fs.watch.\",\n },\n {\n packageName: \"canvas\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"node-canvas relies on native bindings and platform-specific install steps that depend on Bun's lifecycle-script handling.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add canvas to trustedDependencies and verify installs on each target OS, or evaluate @napi-rs/canvas if its API fits.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"cross-fetch\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun provides native Request, Response, Headers, and fetch, making cross-fetch redundant as a polyfill.\",\n sources: [BUN_DOCS.fetch, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"global fetch\",\n migrationHint:\n \"Remove cross-fetch when all target runtimes (including Bun) provide global fetch.\",\n },\n {\n packageName: \"cypress\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"Cypress installs its desktop binary via a postinstall script that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add cypress to trustedDependencies, or run npx cypress install explicitly after bun install.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"dotenv\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun loads .env, .env.local, and environment-specific .env files automatically, making dotenv redundant for the files Bun already reads.\",\n sources: [BUN_DOCS.environmentVariables],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun built-in .env loading\",\n migrationHint:\n \"Remove dotenv/config bootstraps when Bun's automatic .env loading already covers the same files.\",\n },\n {\n packageName: \"esbuild\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Bun ships a native bundler with an esbuild migration guide for projects that only need basic bundling.\",\n sources: [BUN_DOCS.bundler, BUN_DOCS.bundlerEsbuild],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun build\",\n migrationHint:\n \"Evaluate bun build for build scripts whose esbuild config is straightforward (entry + format + outdir).\",\n },\n {\n packageName: \"got\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun ships native fetch and Web HTTP APIs that cover most got use cases, though got continues to work on Bun.\",\n sources: [BUN_DOCS.fetch, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"global fetch\",\n migrationHint:\n \"Evaluate global fetch for simple HTTP flows; keep got where you rely on its retry or hooks model.\",\n },\n {\n packageName: \"ioredis\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"ioredis works on Bun, but Bun ships a native Redis client that avoids the parser/IO overhead of a pure-JS client.\",\n sources: [BUN_DOCS.redis],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.RedisClient\",\n workaround:\n \"Keep ioredis if you rely on its cluster, sentinel, or scripting helpers; otherwise evaluate the native Bun Redis client.\",\n },\n {\n packageName: \"jest\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Jest works on Bun, and bun:test ships natively with Jest-compatible expect/describe/test for most suites.\",\n sources: [BUN_DOCS.testRunner, BUN_DOCS.testConfiguration],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:test\",\n workaround:\n \"Keep Jest if you rely on jest.config transforms, custom resolvers, or jest-specific reporter behavior.\",\n migrationHint:\n \"Audit jest.config and setup files before switching test scripts to bun test.\",\n },\n {\n packageName: \"mocha\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Mocha runs on Bun, but bun:test is the Jest-compatible native alternative that requires rewriting BDD-style suites to adopt.\",\n sources: [BUN_DOCS.testRunner],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:test\",\n workaround:\n \"Keep Mocha if its BDD style or reporter ecosystem matters to the project.\",\n },\n {\n packageName: \"nan\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"nan is the legacy Node Abstractions header used by older native modules; modules depending on it require lifecycle scripts Bun skips unless trusted.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Audit which dependencies pull in nan and add them to trustedDependencies after verifying installs on each target platform.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"node-fetch\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun provides global fetch, making node-fetch redundant as a polyfill.\",\n sources: [BUN_DOCS.fetch, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"global fetch\",\n migrationHint:\n \"Remove node-fetch imports when all target runtimes provide global fetch.\",\n },\n {\n packageName: \"node-gyp\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"node-gyp drives native module builds via lifecycle scripts that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Audit which dependencies need node-gyp at install time and add them to trustedDependencies after verifying on each target platform.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"node-pty\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"node-pty wraps native pseudo-terminal bindings via node-gyp and depends on lifecycle scripts Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add node-pty to trustedDependencies and verify installs on each target OS, especially Windows.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"node-pre-gyp\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"node-pre-gyp resolves prebuilt native binaries via lifecycle scripts that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add affected packages to trustedDependencies, or verify that prebuilt binaries resolve without an install step.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"node-sass\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"node-sass depends on native install steps Bun skips by default and is upstream-deprecated in favor of sass.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n replacement: \"sass\",\n workaround:\n \"Prefer Dart Sass (sass); if you keep node-sass, add it to trustedDependencies and verify platform installs.\",\n migrationHint: \"Replace node-sass with sass where possible.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"nodemon\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun has built-in --watch and --hot modes that cover most nodemon dev-script use cases.\",\n sources: [BUN_DOCS.watchMode],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun --watch or bun --hot\",\n migrationHint:\n \"Replace nodemon dev scripts with bun --watch or bun --hot when their behavior matches.\",\n },\n {\n packageName: \"pm2\",\n severity: \"win\",\n confidence: \"low\",\n reason:\n \"pm2 runs on Bun but is heavier than Bun's --watch/--hot modes for dev loops; production process management remains pm2's strength.\",\n sources: [BUN_DOCS.watchMode],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Keep pm2 for cluster mode, log rotation, or zero-downtime restarts; replace its dev-watch usage with bun --watch.\",\n },\n {\n packageName: \"pg-native\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"pg-native links to libpq and requires native install steps Bun skips by default, while the pure-JS pg client has no such constraint.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n replacement: \"pg\",\n workaround:\n \"If pg-native is required for performance, add it to trustedDependencies and verify install on every target platform.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"playwright\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"Playwright downloads browser binaries via a postinstall script that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add playwright (and browser packages) to trustedDependencies, or run npx playwright install explicitly in CI.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"prisma\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"The Prisma CLI runs schema generation via lifecycle scripts that Bun skips for dependencies by default.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add prisma to trustedDependencies, or run prisma generate explicitly in the build step.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"puppeteer\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"puppeteer downloads Chromium via a postinstall script that Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add puppeteer to trustedDependencies, install Chromium manually, or use puppeteer-core with an existing browser binary.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"redis\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"node-redis (the redis package) works on Bun, but Bun ships a native Redis client that avoids the parser overhead of a pure-JS client.\",\n sources: [BUN_DOCS.redis],\n lastVerified: LAST_VERIFIED,\n replacement: \"Bun.RedisClient\",\n workaround:\n \"Keep node-redis if you rely on its v4 command/feature surface; otherwise evaluate the native Bun Redis client.\",\n },\n {\n packageName: \"serialport\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"serialport ships native bindings whose install paths depend on lifecycle scripts Bun skips for dependencies unless trusted.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add serialport to trustedDependencies and verify installs on each target OS and Node-API ABI.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"sharp\",\n severity: \"risk\",\n confidence: \"medium\",\n reason:\n \"sharp uses native bindings and prebuilds whose install behavior depends on Bun's lifecycle-script handling.\",\n sources: [BUN_DOCS.lifecycle, BUN_DOCS.nodeCompatibility],\n lastVerified: LAST_VERIFIED,\n workaround:\n \"Add sharp to trustedDependencies and verify installs on each target OS and arch (especially Alpine/musl).\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"sqlite3\",\n severity: \"risk\",\n confidence: \"high\",\n reason:\n \"sqlite3 uses native bindings and lifecycle scripts Bun skips by default, while Bun ships a native SQLite driver.\",\n sources: [BUN_DOCS.sqlite, BUN_DOCS.lifecycle],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:sqlite\",\n migrationHint:\n \"Database from bun:sqlite covers most sqlite3 use cases for new Bun-only code.\",\n requiresTrustedDependency: true,\n },\n {\n packageName: \"ts-node\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun executes TypeScript files directly, making ts-node redundant when Bun is the runtime.\",\n sources: [BUN_DOCS.typescript],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun run file.ts\",\n migrationHint:\n \"Replace ts-node scripts with bun run and drop --loader ts-node/esm flags from Bun-targeted scripts.\",\n },\n {\n packageName: \"tsup\",\n severity: \"win\",\n confidence: \"low\",\n reason:\n \"tsup wraps esbuild and works on Bun; Bun's native bundler covers many tsup use cases for libraries with simple build configs.\",\n sources: [BUN_DOCS.bundler, BUN_DOCS.bundlerEsbuild],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun build\",\n workaround:\n \"Keep tsup where you rely on its dts generation, watch mode, or specific build target presets.\",\n },\n {\n packageName: \"tsx\",\n severity: \"win\",\n confidence: \"high\",\n reason:\n \"Bun runs TypeScript files directly with --watch and --hot modes that duplicate tsx's functionality.\",\n sources: [BUN_DOCS.typescript, BUN_DOCS.watchMode],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun run file.ts\",\n migrationHint: \"Replace tsx scripts with bun run when Bun is the target runtime.\",\n },\n {\n packageName: \"vitest\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Vitest runs on Bun, but bun:test is the Jest-compatible native alternative whose suitability depends on which Vitest features the suite uses.\",\n sources: [BUN_DOCS.testRunner],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun:test\",\n workaround:\n \"Keep Vitest if you rely on its Vite plugin pipeline, browser mode, or specific snapshot/coverage behavior.\",\n },\n {\n packageName: \"webpack\",\n severity: \"win\",\n confidence: \"medium\",\n reason:\n \"Bun includes a native bundler that covers many application and library workflows webpack handles.\",\n sources: [BUN_DOCS.bundler],\n lastVerified: LAST_VERIFIED,\n replacement: \"bun build\",\n migrationHint:\n \"Evaluate bun build for simple library/app bundles; keep webpack where you rely on loaders or plugins bun build doesn't cover.\",\n },\n];\n","import path from \"node:path\";\nimport { BUN_DOCS } from \"./constants.js\";\nimport { COMPAT_DB } from \"./compat-db.js\";\nimport type {\n CompatEntry,\n Diagnostic,\n FindingCategory,\n FindingLevel,\n PackageManifest,\n ProjectInfo,\n} from \"./types.js\";\nimport { escapeRegExp, findLineNumber, isPlainObject } from \"./utils.js\";\n\nconst CATEGORY_BY_LEVEL: Record<FindingLevel, FindingCategory> = {\n blocker: \"Blockers\",\n risk: \"Risks\",\n migration: \"Migration work\",\n win: \"Bun wins\",\n};\n\ninterface DiagnosticInput {\n ruleId: string;\n title: string;\n level: FindingLevel;\n message: string;\n filePath: string;\n sources: string[];\n line?: number;\n help?: string;\n packageName?: string;\n replacement?: string;\n alsoIn?: string[];\n}\n\ninterface CodeRule {\n ruleId: string;\n title: string;\n level: FindingLevel;\n pattern: RegExp;\n requiresPattern?: RegExp;\n message: string;\n sources: string[];\n help?: string;\n}\n\nconst createDiagnostic = (input: DiagnosticInput): Diagnostic => {\n if (input.sources.length === 0) {\n throw new Error(`Rule ${input.ruleId} is missing a source`);\n }\n return {\n ruleId: input.ruleId,\n title: input.title,\n level: input.level,\n category: CATEGORY_BY_LEVEL[input.level],\n message: input.message,\n filePath: input.filePath,\n line: input.line ?? 1,\n sources: input.sources,\n help: input.help,\n packageName: input.packageName,\n replacement: input.replacement,\n alsoIn: input.alsoIn,\n };\n};\n\nconst buildCompatHelp = (entry: CompatEntry, needsTrust: boolean): string | undefined => {\n if (needsTrust) {\n return `Add ${entry.packageName} to trustedDependencies before relying on its install scripts.`;\n }\n return entry.migrationHint ?? entry.workaround;\n};\n\nconst findKeyLine = (content: string | null, key: string): number => {\n if (!content) return 1;\n return findLineNumber(content, new RegExp(`\"${escapeRegExp(key)}\"\\\\s*:`));\n};\n\nconst findYamlKeyLine = (content: string | null, key: string): number => {\n if (!content) return 1;\n return findLineNumber(content, new RegExp(`^\\\\s*${escapeRegExp(key)}\\\\s*:`, \"m\"));\n};\n\nconst findCatalogReferenceLine = (manifestContent: string | null): number => {\n if (!manifestContent) return 1;\n return findLineNumber(manifestContent, /\"catalog:/);\n};\n\nconst findDependencyLine = (manifestContent: string, packageName: string): number =>\n findKeyLine(manifestContent, packageName);\n\nconst createCompatDiagnostic = (\n entry: CompatEntry,\n manifest: PackageManifest,\n needsTrust: boolean,\n alsoIn?: string[],\n): Diagnostic =>\n createDiagnostic({\n ruleId: `compat/${entry.packageName}`,\n title: `${entry.packageName} compatibility note`,\n level: entry.severity,\n message: entry.reason,\n filePath: manifest.packageJsonPath,\n line: findDependencyLine(manifest.manifestContent, entry.packageName),\n sources: entry.sources,\n packageName: entry.packageName,\n replacement: entry.replacement,\n help: buildCompatHelp(entry, needsTrust),\n alsoIn,\n });\n\nconst getCompilerOptions = (project: ProjectInfo): Record<string, unknown> => {\n const compilerOptions = project.tsconfig?.compilerOptions;\n return isPlainObject(compilerOptions) ? compilerOptions : {};\n};\n\nconst usesBunGlobal = (project: ProjectInfo): boolean =>\n project.sourceFiles.some((sourceFile) => /\\bBun\\.|from\\s+[\"']bun[\"']|require\\([\"']bun[\"']\\)/.test(sourceFile.content));\n\nconst hasDependency = (project: ProjectInfo, packageName: string): boolean =>\n Boolean(project.dependencies[packageName]);\n\nconst findDependencyManifests = (project: ProjectInfo, packageName: string): PackageManifest[] =>\n project.packageManifests.filter((manifest) => Boolean(manifest.dependencies[packageName]));\n\nconst hasPackageJsonWorkspaces = (project: ProjectInfo): boolean => Boolean(project.packageJson.workspaces);\n\nconst hasCatalogReference = (project: ProjectInfo): boolean =>\n Object.values(project.dependencies).some((version) => version.startsWith(\"catalog:\"));\n\nconst hasPackageJsonCatalog = (project: ProjectInfo): boolean =>\n Boolean(project.packageJson.catalog) ||\n Boolean(project.packageJson.catalogs) ||\n (isPlainObject(project.packageJson.workspaces) &&\n (Boolean(project.packageJson.workspaces.catalog) || Boolean(project.packageJson.workspaces.catalogs)));\n\nconst workflowUsesBun = (content: string): boolean => /\\bbun\\s+(install|run|test|build|x)\\b/.test(content);\nconst workflowUsesSetupBun = (content: string): boolean => /oven-sh\\/setup-bun@/.test(content);\nconst workflowUsesLegacyInstall = (content: string): boolean =>\n /\\b(npm ci|npm install|pnpm install|yarn install|yarn --frozen-lockfile)\\b/.test(content);\nconst workflowUsesUnfrozenBunInstall = (content: string): boolean =>\n /^\\s*(-\\s*)?run:\\s*bun install\\s*$/m.test(content) || /\\bbun install\\b(?![^\\n]*--frozen-lockfile)/.test(content);\n\nexport const runPackageRules = (project: ProjectInfo): Diagnostic[] => {\n const diagnostics: Diagnostic[] = [];\n const packageJsonPath = project.packageJsonPath;\n const rootManifestContent = project.packageManifests[0]?.manifestContent ?? \"\";\n const hasBunLock = project.lockfiles.includes(\"bun.lock\");\n const hasBunLockb = project.lockfiles.includes(\"bun.lockb\");\n const hasAnyBunLock = hasBunLock || hasBunLockb;\n\n if (!hasAnyBunLock) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/lockfile-missing\",\n title: \"Missing Bun lockfile\",\n level: \"migration\",\n message: \"This project has no bun.lock, so installs and Bun CI cannot be treated as reproducible.\",\n filePath: packageJsonPath,\n sources: [BUN_DOCS.lockfile],\n help: \"Run bun install and commit bun.lock.\",\n }),\n );\n }\n\n if (hasBunLockb) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/legacy-lockb\",\n title: \"Legacy binary Bun lockfile\",\n level: \"migration\",\n message: \"bun.lockb is the legacy binary lockfile format, superseded by the text-based bun.lock in Bun v1.2+.\",\n filePath: path.join(project.rootDirectory, \"bun.lockb\"),\n sources: [BUN_DOCS.lockfile],\n help: \"Migrate with bun install --save-text-lockfile --frozen-lockfile --lockfile-only, then remove bun.lockb after verification.\",\n }),\n );\n }\n\n if (hasAnyBunLock && project.legacyLockfiles.length > 0) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/mixed-lockfiles\",\n title: \"Mixed package-manager lockfiles\",\n level: \"risk\",\n message: `Bun lockfile coexists with ${project.legacyLockfiles.join(\", \")}, leaving dependency resolution ownership ambiguous.`,\n filePath: packageJsonPath,\n sources: [BUN_DOCS.lockfile],\n help: \"Keep legacy lockfiles only if another supported workflow still owns them; otherwise remove them after validating bun.lock.\",\n }),\n );\n }\n\n if (!project.packageJson.packageManager?.startsWith(\"bun@\")) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/package-manager-field\",\n title: \"packageManager does not pin Bun\",\n level: \"migration\",\n message: \"package.json does not pin Bun in packageManager, so contributors and CI may use different package managers.\",\n filePath: packageJsonPath,\n line: project.packageJson.packageManager ? findKeyLine(rootManifestContent, \"packageManager\") : 1,\n sources: [BUN_DOCS.lockfile],\n help: \"Set packageManager to the Bun version used by the project, for example bun@1.3.11.\",\n }),\n );\n }\n\n if (project.pnpmWorkspacePath && !hasPackageJsonWorkspaces(project)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/pnpm-workspace-only\",\n title: \"Workspaces only declared for pnpm\",\n level: \"blocker\",\n message: \"Bun reads workspaces from package.json, so a pnpm-workspace.yaml without a matching package.json workspaces entry will not define Bun workspaces.\",\n filePath: project.pnpmWorkspacePath,\n line: findYamlKeyLine(project.pnpmWorkspaceContent, \"packages\"),\n sources: [BUN_DOCS.workspaces],\n help: \"Move workspace globs into package.json workspaces before relying on bun install at the repo root.\",\n }),\n );\n }\n\n if (hasCatalogReference(project) && !hasPackageJsonCatalog(project)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/catalog-without-package-json-catalog\",\n title: \"Catalog references need Bun catalog definitions\",\n level: \"blocker\",\n message: \"This project uses catalog: dependency references, but no Bun catalog or catalogs definition was found in package.json.\",\n filePath: packageJsonPath,\n line: findCatalogReferenceLine(rootManifestContent),\n sources: [BUN_DOCS.catalogs],\n help: \"Define catalog or catalogs in package.json, preferably under workspaces for monorepos.\",\n }),\n );\n }\n\n for (const workflow of project.workflows) {\n if (workflowUsesBun(workflow.content) && !workflowUsesSetupBun(workflow.content)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/ci-missing-setup-bun\",\n title: \"CI uses Bun without setup-bun\",\n level: \"blocker\",\n message: \"This workflow runs bun commands but does not install Bun with oven-sh/setup-bun.\",\n filePath: workflow.filePath,\n line: findLineNumber(workflow.content, /\\bbun\\s+(install|run|test|build|x)\\b/),\n sources: [BUN_DOCS.ci],\n help: \"Add oven-sh/setup-bun before bun commands in GitHub Actions.\",\n }),\n );\n }\n\n if (hasAnyBunLock && workflowUsesLegacyInstall(workflow.content)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/ci-uses-legacy-package-manager\",\n title: \"CI still installs with another package manager\",\n level: \"migration\",\n message: \"This workflow uses npm, pnpm, or yarn install even though the project has a Bun lockfile.\",\n filePath: workflow.filePath,\n line: findLineNumber(workflow.content, /\\b(npm ci|npm install|pnpm install|yarn install|yarn --frozen-lockfile)\\b/),\n sources: [BUN_DOCS.ci],\n help: \"Switch Bun-owned CI jobs to bun install --frozen-lockfile.\",\n }),\n );\n }\n\n if (workflowUsesUnfrozenBunInstall(workflow.content)) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/ci-install-not-frozen\",\n title: \"CI Bun install is not frozen\",\n level: \"risk\",\n message: \"This workflow runs bun install without --frozen-lockfile, so CI can update bun.lock instead of verifying it.\",\n filePath: workflow.filePath,\n line: findLineNumber(workflow.content, /\\bbun install\\b/),\n sources: [BUN_DOCS.bunfig, BUN_DOCS.ci],\n help: \"Use bun install --frozen-lockfile in CI.\",\n }),\n );\n }\n }\n\n if (usesBunGlobal(project) && !hasDependency(project, \"@types/bun\")) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/types-package-missing\",\n title: \"Bun types are missing\",\n level: \"migration\",\n message: \"Source files reference Bun APIs, but @types/bun is not installed.\",\n filePath: packageJsonPath,\n sources: [BUN_DOCS.typescript],\n help: \"Install @types/bun as a dev dependency.\",\n }),\n );\n }\n\n const compilerOptions = getCompilerOptions(project);\n const compilerTypes = compilerOptions.types;\n if (hasDependency(project, \"@types/bun\") && Array.isArray(compilerTypes) && !compilerTypes.includes(\"bun\")) {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/tsconfig-types-missing-bun\",\n title: \"tsconfig types excludes Bun\",\n level: \"risk\",\n message: \"@types/bun is installed, but compilerOptions.types does not include bun. TypeScript 6+ requires explicit Bun types in this mode.\",\n filePath: project.tsconfigPath ?? packageJsonPath,\n line: findKeyLine(project.tsconfigContent, \"types\"),\n sources: [BUN_DOCS.typescript],\n help: \"Add \\\"bun\\\" to compilerOptions.types or remove types if you do not need to restrict global type packages.\",\n }),\n );\n }\n\n if (project.bunfig?.installAuto && project.bunfig.installAuto !== \"disable\") {\n diagnostics.push(\n createDiagnostic({\n ruleId: \"bun/auto-install-enabled\",\n title: \"Bun auto-install is enabled\",\n level: \"risk\",\n message: `bunfig.toml sets install.auto to ${project.bunfig.installAuto}, letting Bun fetch packages during execution when node_modules is absent.`,\n filePath: project.bunfig.filePath,\n line: findLineNumber(project.bunfig.content, /auto\\s*=/),\n sources: [BUN_DOCS.autoInstall, BUN_DOCS.bunfig],\n help: \"For application repos and CI, consider install.auto = \\\"disable\\\" for more predictable execution.\",\n }),\n );\n }\n\n for (const entry of COMPAT_DB) {\n const matchingManifests = findDependencyManifests(project, entry.packageName);\n if (matchingManifests.length === 0) continue;\n\n if (entry.severity === \"win\") {\n const [primaryManifest, ...otherManifests] = matchingManifests;\n if (!primaryManifest) continue;\n const isTrustedEverywhere = matchingManifests.every((manifest) =>\n manifest.trustedDependencies.has(entry.packageName),\n );\n const needsTrust = Boolean(entry.requiresTrustedDependency) && !isTrustedEverywhere;\n const alsoIn = otherManifests.map((manifest) => manifest.packageJsonPath);\n diagnostics.push(createCompatDiagnostic(entry, primaryManifest, needsTrust, alsoIn));\n continue;\n }\n\n for (const manifest of matchingManifests) {\n const needsTrust =\n Boolean(entry.requiresTrustedDependency) && !manifest.trustedDependencies.has(entry.packageName);\n diagnostics.push(createCompatDiagnostic(entry, manifest, needsTrust));\n }\n }\n\n return diagnostics;\n};\n\nconst CODE_RULES: CodeRule[] = [\n {\n ruleId: \"code/node-repl\",\n title: \"node:repl is not implemented in Bun\",\n level: \"blocker\",\n pattern: /(?:from\\s+[\"']node:repl[\"']|require\\([\"']node:repl[\"']\\))/,\n message: \"Bun's Node compatibility table marks node:repl as not implemented.\",\n sources: [BUN_DOCS.nodeCompatibility],\n },\n {\n ruleId: \"code/node-trace-events\",\n title: \"node:trace_events is not implemented in Bun\",\n level: \"blocker\",\n pattern: /(?:from\\s+[\"']node:trace_events[\"']|require\\([\"']node:trace_events[\"']\\))/,\n message: \"Bun's Node compatibility table marks node:trace_events as not implemented.\",\n sources: [BUN_DOCS.nodeCompatibility],\n },\n {\n ruleId: \"code/node-sqlite\",\n title: \"node:sqlite is not implemented in Bun\",\n level: \"blocker\",\n pattern: /(?:from\\s+[\"']node:sqlite[\"']|require\\([\"']node:sqlite[\"']\\))/,\n message: \"Bun does not implement node:sqlite. Bun provides bun:sqlite instead.\",\n sources: [BUN_DOCS.nodeCompatibility, BUN_DOCS.sqlite],\n help: \"Use bun:sqlite for Bun-targeted SQLite code.\",\n },\n {\n ruleId: \"code/process-binding\",\n title: \"process.binding usage is compatibility-sensitive\",\n level: \"risk\",\n pattern: /\\bprocess\\.binding\\s*\\(/,\n message: \"process.binding is an internal Node API and only partially implemented by Bun.\",\n sources: [BUN_DOCS.nodeCompatibility],\n help: \"Replace internal Node binding access with public APIs before migrating.\",\n },\n {\n ruleId: \"code/process-missing-api\",\n title: \"Node process API is not implemented in Bun\",\n level: \"blocker\",\n pattern: /\\bprocess\\.(loadEnvFile|getBuiltinModule)\\s*\\(/,\n message: \"Bun marks process.loadEnvFile and process.getBuiltinModule as not implemented in its Node compatibility docs.\",\n sources: [BUN_DOCS.nodeCompatibility],\n },\n {\n ruleId: \"code/module-register\",\n title: \"module.register is not implemented in Bun\",\n level: \"blocker\",\n pattern: /\\bmodule\\.register\\s*\\(/,\n message: \"Bun lists module.register as not implemented and recommends Bun.plugin instead.\",\n sources: [BUN_DOCS.nodeCompatibility, BUN_DOCS.plugins],\n help: \"Evaluate Bun.plugin or avoid runtime module loader hooks in Bun-targeted code.\",\n },\n {\n ruleId: \"code/node-test\",\n title: \"node:test is only partly implemented in Bun\",\n level: \"migration\",\n pattern: /(?:from\\s+[\"']node:test[\"']|require\\([\"']node:test[\"']\\))/,\n message: \"Bun marks node:test as partly implemented and provides bun:test as its native runner.\",\n sources: [BUN_DOCS.nodeCompatibility, BUN_DOCS.testRunner],\n help: \"Prefer bun:test when migrating the test runner to Bun.\",\n },\n {\n ruleId: \"code/v8-specific-api\",\n title: \"V8-specific APIs are compatibility-sensitive\",\n level: \"risk\",\n pattern: /(?:from\\s+[\"']node:v8[\"']|require\\([\"']node:v8[\"']\\)|\\bv8\\.(serialize|deserialize|setFlagsFromString|cachedDataVersionTag)\\s*\\()/,\n message: \"Bun runs on JavaScriptCore, and its Node v8 compatibility is partial.\",\n sources: [BUN_DOCS.nodeCompatibility],\n help: \"Avoid V8-specific runtime assumptions in Bun-targeted code.\",\n },\n {\n ruleId: \"code/worker-resource-limits\",\n title: \"worker_threads resource limits are unsupported\",\n level: \"risk\",\n pattern: /\\bresourceLimits\\s*:/,\n requiresPattern: /(?:from\\s+[\"']node:worker_threads[\"']|require\\([\"']node:worker_threads[\"']\\)|from\\s+[\"']worker_threads[\"']|require\\([\"']worker_threads[\"']\\)|\\bnew\\s+Worker\\s*\\()/,\n message: \"Bun marks worker_threads resourceLimits as unsupported in its Node compatibility notes.\",\n sources: [BUN_DOCS.nodeCompatibility],\n help: \"Verify worker behavior under Bun or avoid Node-specific Worker options.\",\n },\n];\n\nexport const runCodeRules = (project: ProjectInfo): Diagnostic[] => {\n const diagnostics: Diagnostic[] = [];\n\n for (const sourceFile of project.sourceFiles) {\n for (const rule of CODE_RULES) {\n if (rule.requiresPattern && !rule.requiresPattern.test(sourceFile.content)) continue;\n if (!rule.pattern.test(sourceFile.content)) continue;\n diagnostics.push(\n createDiagnostic({\n ruleId: rule.ruleId,\n title: rule.title,\n level: rule.level,\n message: rule.message,\n filePath: sourceFile.filePath,\n line: findLineNumber(sourceFile.content, rule.pattern),\n sources: rule.sources,\n help: rule.help,\n }),\n );\n }\n }\n\n return diagnostics;\n};\n","import {\n BLOCKER_RULE_PENALTY,\n MIGRATION_RULE_PENALTY,\n PERFECT_SCORE,\n RISK_RULE_PENALTY,\n SCORE_CLOSE_THRESHOLD,\n SCORE_READY_THRESHOLD,\n SCORE_RISKY_THRESHOLD,\n} from \"./constants.js\";\nimport type { Diagnostic, FindingLevel, ScanSummary, ScoreResult } from \"./types.js\";\n\nconst getScoreLabel = (score: number): ScoreResult[\"label\"] => {\n if (score >= SCORE_READY_THRESHOLD) return \"Ready\";\n if (score >= SCORE_CLOSE_THRESHOLD) return \"Close\";\n if (score >= SCORE_RISKY_THRESHOLD) return \"Risky\";\n return \"Blocked\";\n};\n\nconst collectUniqueRuleCounts = (diagnostics: Diagnostic[]): Record<FindingLevel, Set<string>> => {\n const counts: Record<FindingLevel, Set<string>> = {\n blocker: new Set(),\n risk: new Set(),\n migration: new Set(),\n win: new Set(),\n };\n\n for (const diagnostic of diagnostics) {\n counts[diagnostic.level].add(diagnostic.ruleId);\n }\n\n return counts;\n};\n\nexport const calculateScore = (diagnostics: Diagnostic[]): ScoreResult => {\n const counts = collectUniqueRuleCounts(diagnostics);\n const penalty =\n counts.blocker.size * BLOCKER_RULE_PENALTY +\n counts.risk.size * RISK_RULE_PENALTY +\n counts.migration.size * MIGRATION_RULE_PENALTY;\n const score = Math.max(0, Math.round(PERFECT_SCORE - penalty));\n return { score, label: getScoreLabel(score) };\n};\n\nexport const summarizeDiagnostics = (diagnostics: Diagnostic[]): ScanSummary => ({\n blockers: diagnostics.filter((diagnostic) => diagnostic.level === \"blocker\").length,\n risks: diagnostics.filter((diagnostic) => diagnostic.level === \"risk\").length,\n migrations: diagnostics.filter((diagnostic) => diagnostic.level === \"migration\").length,\n wins: diagnostics.filter((diagnostic) => diagnostic.level === \"win\").length,\n});\n","import { loadConfig, filterIgnoredDiagnostics } from \"./config.js\";\nimport { discoverProject } from \"./project.js\";\nimport { runCodeRules, runPackageRules } from \"./rules.js\";\nimport { calculateScore, summarizeDiagnostics } from \"./score.js\";\nimport type { ScanOptions, ScanResult } from \"./types.js\";\n\nexport const scan = async (directory: string, options: ScanOptions = {}): Promise<ScanResult> => {\n const project = discoverProject(directory);\n const loadedConfig = options.configOverride ?? loadConfig(project.rootDirectory, project.packageJson);\n const shouldRunPackageChecks = options.packageChecks ?? loadedConfig.package ?? true;\n const shouldRunCodeChecks = options.codeChecks ?? loadedConfig.code ?? true;\n\n const diagnostics = [\n ...(shouldRunPackageChecks ? runPackageRules(project) : []),\n ...(shouldRunCodeChecks ? runCodeRules(project) : []),\n ];\n const filteredDiagnostics = filterIgnoredDiagnostics(\n diagnostics,\n loadedConfig,\n project.rootDirectory,\n );\n\n return {\n project,\n diagnostics: filteredDiagnostics,\n score: calculateScore(filteredDiagnostics),\n summary: summarizeDiagnostics(filteredDiagnostics),\n };\n};\n"],"mappings":"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CF,SAAS,CAAC,SAAS,OAAO,SAAS,kBAAkB;EACrD,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,UAAU;EAC7B,cAAc;EACd,YACE;EACF,2BAA2B;EAC5B;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,qBAAqB;EACxC,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,SAAS,SAAS,eAAe;EACpD,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,OAAO,SAAS,kBAAkB;EACrD,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,MAAM;EACzB,cAAc;EACd,aAAa;EACb,YACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,YAAY,SAAS,kBAAkB;EAC1D,cAAc;EACd,aAAa;EACb,YACE;EACF,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,WAAW;EAC9B,cAAc;EACd,aAAa;EACb,YACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,WAAW,SAAS,kBAAkB;EACzD,cAAc;EACd,YACE;EACF,2BAA2B;EAC5B;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,OAAO,SAAS,kBAAkB;EACrD,cAAc;EACd,aAAa;EACb,eACE;EACH;CACD;EACE,aAAa;EACb,UAAU;EACV,YAAY;EACZ,QACE;EACF,SAAS,CAAC,SAAS,UAAU;EAC7B,cAAc;EACd,YACE;EACF,2BA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"}