Skip to content

Commit 29bf9cb

Browse files
betegonclaude
andcommitted
fix(init): use Bun.build for ink-app sidecar pre-bundling
esbuild wraps CJS packages (signal-exit, parse-keypress, etc.) in __commonJS helpers. When Bun.compile then embeds the resulting file as a with { type: "file" } asset, it injects __promiseAll helpers at wrong positions inside those wrappers, causing: SyntaxError: Unexpected identifier '__promiseAll' at parse-keypress.js:420:1 on all platforms. Switching to Bun.build produces output that Bun.compile recognises natively and processes without the mis-injection. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent e7d8b9b commit 29bf9cb

1 file changed

Lines changed: 27 additions & 34 deletions

File tree

script/text-import-plugin.ts

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@
2626
* like `ink-frame.tsx` and third-party packages like `ink`,
2727
* `react`) so the file is fully self-contained.
2828
*
29-
* Some of the inlined packages (e.g. `signal-exit`, used by Ink)
30-
* are CJS modules that call `require("assert")` etc. esbuild
31-
* wraps these in `__require` shims that throw "Dynamic require
32-
* is not supported" at runtime. The banner injects a real
33-
* `require` function via `createRequire` so CJS dependencies
34-
* resolve Node builtins correctly inside the ESM bundle.
35-
*
3629
* Non-TypeScript files (plain `.js`) are copied verbatim.
3730
*
3831
* Used by `script/build.ts` (single-file executable) and
@@ -44,7 +37,7 @@
4437

4538
import { copyFileSync, mkdirSync, readFileSync } from "node:fs";
4639
import { basename, dirname, extname, resolve as resolvePath } from "node:path";
47-
import { build as esbuildBuild, type Plugin } from "esbuild";
40+
import type { Plugin } from "esbuild";
4841

4942
const TEXT_IMPORT_NS = "text-import";
5043
const ANY_FILTER = /.*/;
@@ -53,36 +46,36 @@ const ANY_FILTER = /.*/;
5346
const TS_EXTENSIONS = new Set([".ts", ".tsx", ".jsx"]);
5447

5548
/**
56-
* Banner injected into the pre-bundled sidecar JS. Provides a real
57-
* `require` function so esbuild's CJS-wrapping `__require` shims
58-
* can resolve Node.js builtins (`assert`, `events`, etc.) at runtime.
59-
* Without this, `signal-exit` and other CJS deps of Ink throw
60-
* "Dynamic require of 'assert' is not supported".
61-
*/
62-
const REQUIRE_BANNER =
63-
'import { createRequire as ___cr } from "node:module";' +
64-
" var require = ___cr(import.meta.url);";
65-
66-
/**
67-
* Pre-bundle a TypeScript/TSX source file into self-contained JS.
68-
* All dependencies (local modules AND npm packages) are inlined;
69-
* only `node:*` builtins are external since Bun resolves them
70-
* natively inside `/$bunfs/`.
49+
* Pre-bundle a TypeScript/TSX source file into a self-contained JS module
50+
* using Bun.build. All dependencies (local modules AND npm packages) are
51+
* inlined; Bun handles node:* builtins natively so no explicit externals
52+
* are needed.
53+
*
54+
* Using Bun.build (rather than esbuild) is critical. esbuild wraps CJS
55+
* packages (e.g. `signal-exit`, `parse-keypress`) in `__commonJS` helpers.
56+
* When Bun.compile later embeds the esbuild output as a `with { type: "file"
57+
* }` asset, it injects `__promiseAll` helpers at wrong positions inside those
58+
* wrappers, producing `SyntaxError: Unexpected identifier '__promiseAll'` at
59+
* runtime. Bun.build produces output that Bun.compile recognises natively and
60+
* handles without mis-injecting the helper.
7161
*/
7262
async function prebundleTs(sourcePath: string, outPath: string): Promise<void> {
73-
await esbuildBuild({
74-
entryPoints: [sourcePath],
75-
bundle: true,
76-
outfile: outPath,
77-
platform: "node",
78-
target: "esnext",
79-
format: "esm",
80-
jsx: "automatic",
81-
external: ["node:*"],
82-
banner: { js: REQUIRE_BANNER },
63+
const outdir = dirname(outPath);
64+
const result = await Bun.build({
65+
entrypoints: [sourcePath],
66+
target: "bun",
67+
outdir,
68+
naming: "[name].js",
69+
define: {
70+
"process.env.NODE_ENV": JSON.stringify("production"),
71+
},
8372
minify: false,
84-
write: true,
8573
});
74+
if (!result.success) {
75+
throw new Error(
76+
result.logs.map((l) => String(l)).join("\n") || "unknown error"
77+
);
78+
}
8679
}
8780

8881
/** Resolve the output directory from the parent esbuild config. */

0 commit comments

Comments
 (0)