Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion packages/opencode/script/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,24 @@ for (const item of targets) {
const bunfsRoot = item.os === "win32" ? "B:/~BUN/root/" : "/$bunfs/root/"
const workerRelativePath = path.relative(dir, parserWorker).replaceAll("\\", "/")

// kilocode_change start - redirect @morphllm/morphsdk ESM barrel to self-contained CJS bundle
// Bun 1.3.14 (with conditions:["browser"]) resolves via the "import" condition, pulling in the
// pre-split ESM barrel (client.js) whose 52 chunk-*.js side-imports make the ESM splitter emit
// invalid minified output: SyntaxError: Exported binding 'G9' needs to refer to a top-level...
// Redirecting onResolve to client.cjs (2300-line self-contained CJS bundle) bypasses the splitter.
const morphsdkCjsPlugin: import("bun").BunPlugin = {
name: "morphsdk-cjs",
setup(build) {
build.onResolve({ filter: /^@morphllm\/morphsdk\/tools\/warp-grep\/client$/ }, () => ({
path: require.resolve("@morphllm/morphsdk/dist/tools/warp_grep/client.cjs"),
}))
},
}
// kilocode_change end
await Bun.build({
conditions: ["browser"],
tsconfig: "./tsconfig.json",
plugins: [plugin], // kilocode_change
plugins: [plugin, morphsdkCjsPlugin], // kilocode_change
// kilocode_change start - skip sourcemaps for release builds (each .js.map adds ~50 MB per target → ~600 MB total)
sourcemap: Script.release ? "none" : "external",
// kilocode_change end
Expand Down
53 changes: 20 additions & 33 deletions packages/opencode/src/kilocode/compat/morphsdk.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,29 @@
import { createRequire } from "module"

// Force Bun to load @morphllm/morphsdk/tools/warp-grep/client via its self-contained
// CJS bundle instead of the pre-split ESM barrel.
//
// WHY THIS EXISTS
// ---------------
// @morphllm/morphsdk ships a pre-split ESM distribution:
// dist/tools/warp_grep/client.js (805 bytes, barrel)
// └─ imports from ../../chunk-P7G3CJB2.js
// └─ side-effects ../../chunk-63VHBANJ.js ... (12 more chunks, 52 total)
//
// When Bun bundles the CLI with `splitting: true + minify: true`, it merges
// these external pre-split chunks into its own chunk graph. Bun 1.3.14 intermittently
// generates invalid minified ESM in that process:
//
// kilocode_change - new file
// Re-exports from @morphllm/morphsdk/tools/warp-grep/client.
//
// WHY THIS INDIRECTION EXISTS
// ----------------------------
// @morphllm/morphsdk ships a pre-split ESM distribution for this path:
// dist/tools/warp_grep/client.js (805-byte barrel)
// └─ imports from ../../chunk-P7G3CJB2.js ... (52 total pre-split chunks)
//
// Bun 1.3.14 bundling with `conditions: ["browser"]` resolves via the "import" condition
// (ESM barrel) even inside createRequire() calls. When its ESM splitter merges those
// external pre-split chunks into the bundle, it generates invalid minified output:
// SyntaxError: Exported binding 'G9' needs to refer to a top-level declared variable.
//
// The error is non-deterministic (Bun's parallel bundler uses different orderings
// per run), so the build sometimes succeeds and sometimes fails on Windows x64.
//
// The CJS bundle (client.cjs, ~2300 lines) is fully self-contained with no external
// chunk imports. `createRequire` lets Bun inline the CJS module directly without
// running it through the ESM splitter.
// FIX: script/build.ts adds a morphsdkCjsPlugin (onResolve) that redirects this module
// specifier to client.cjs — a fully self-contained CJS bundle (~2300 lines, no chunk-*.js
// imports). The plugin runs at bundle time before the ESM splitter is invoked.
//
// HOW TO DETECT THIS FOR FUTURE DEPS
// -----------------------------------
// If a new dependency causes `SyntaxError: Exported binding '...' needs to refer to
// a top-level declared variable` in release builds, check whether its ESM entry point
// is a barrel that re-imports from internal `chunk-*.js` files:
// ------------------------------------
// If a new dependency causes the SyntaxError above in release builds, check whether its
// ESM entry point is a barrel that re-imports from internal `chunk-*.js` files:
//
// head -5 node_modules/<pkg>/dist/index.js
// → imports { ... } from "./chunk-XYZ123.js" ← pre-split ESM
//
// If so, add a CJS bridge here and re-export from it instead of importing the package
// directly. Always verify there is a `.cjs` (or CJS `main`) alternative.
const req = createRequire(import.meta.url)

// Type-cast via the package's own .d.ts so callers get full type safety.
const mod = req("@morphllm/morphsdk/tools/warp-grep/client") as typeof import("@morphllm/morphsdk/tools/warp-grep/client")
// If so, add a matching onResolve redirect to the CJS counterpart in build.ts.

export const WarpGrepClient = mod.WarpGrepClient
export { WarpGrepClient } from "@morphllm/morphsdk/tools/warp-grep/client"
Loading