fix(cli): add Node-compatible bin wrapper for npm publish#1636
Merged
Conversation
npm strips .ts files from bin entries. Added a .mjs wrapper that: - Runs directly when invoked by Bun (imports src/bin.ts) - Re-execs itself with Bun when invoked by Node - Prints install instructions if Bun is not available Pattern follows trekker, t1code, and other Bun-first CLIs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
npm publishstrips.tsfiles from thebinfield. Our CLI's bin pointed to./src/bin.ts, so after publish,npx epicenterandbunx epicenterwould fail — npm silently removes the bin entry from the published package.Solution
Added
bin/epicenter.mjs— a Node-compatible wrapper that handles three runtime scenarios:Running under Bun (the happy path): The wrapper detects
typeof Bun !== "undefined"and directly imports../src/bin.ts. Zero overhead.Running under Node, Bun installed: The wrapper detects it's in Node, checks if Bun is on PATH via
spawnSync("bun", ["--version"]), and re-execs itself with Bun. This handlesnpx epicenteron machines that have both runtimes.Running under Node, no Bun: Prints a clear error message with install instructions and exits.
Why
#!/usr/bin/env nodeinstead of#!/usr/bin/env bunnpxon Windows ignores shebangs and always invokes with Node. A#!/usr/bin/env bunshebang would produce a cryptic OS-level error (bun: No such file or directory) on machines without Bun. Usingnodeas the entry point guarantees the script always launches, then makes an intelligent decision about how to proceed.Why not compile to JS
The CLI imports
.tsfiles and uses Bun-specific APIs (bun:sqlite). Compiling to JavaScript wouldn't help — the output still can't run on Node. Bun is a genuine runtime requirement, not a build preference.Prior art
This is the established pattern for Bun-first CLIs on npm:
#!/usr/bin/env node+ Bun detect + re-execTested
bun epicenter.mjs --helpnode epicenter.mjs --helpChangelog