@cloudflare/worker-bundler@0.1.2
cloudflare/agents@cloudflare/worker-bundler@0.1.2Apr 18, 2026by github-actions[bot]
AI Summary
This patch release adds extensive esbuild configuration options to `createWorker` and `createApp` (including jsx mode, jsxImportSource, define, loader overrides, and conditions), along with an escape hatch for advanced plugins. It also fixes a critical bug where importing the bundler from Node.js caused a confusing 'Cannot find package gojs' error by making the WASM import lazy and adding runtime detection.
Key Highlights
- Added esbuild configuration options: jsx mode, jsxImportSource, define, loader overrides, and conditions
- Introduced `__dangerouslyUseEsBuildPluginsDoNotUseOrYouWillBeFired` escape hatch for advanced use cases like RSC transforms
- Fixed Node.js import crash by making esbuild.wasm import lazy and adding Cloudflare-Workers runtime detection
- `createWorker({ bundle: false })` now works in Node.js (transform-only mode)
- Improved error messages with actionable context for entry points, package.json, npm registry errors, and timeouts
New Features
- New `jsx` option for JSX transformation mode ("transform" | "preserve" | "automatic")
- New `jsxImportSource` option for custom JSX pragma
- New `define` option for compile-time constant replacement
- New `loader` option for per-extension loader overrides with portable BundlerLoader set
- New `conditions` option for package export conditions (e.g. ["workerd", "worker", "browser"])
- Lazy loading of esbuild.wasm to support Node.js environments
- Runtime detection to throw actionable error when used outside Cloudflare Workers
- Transform-only mode (`bundle: false`) now works in Node.js
Full Release Notes
### Patch Changes
- [#1334](https://github.com/cloudflare/agents/pull/1334) [`77c8c9c`](https://github.com/cloudflare/agents/commit/77c8c9c44fd87b9d4fe37639b026adb0cbced8d7) Thanks [@threepointone](https://github.com/threepointone)! - `createWorker` and `createApp` now accept a handful of extra esbuild knobs that previously required forking or patching the package:
- `jsx` (`"transform" | "preserve" | "automatic"`)
- `jsxImportSource`
- `define` (compile-time constant replacement)
- `loader` (per-extension loader overrides — e.g. `{ ".svg": "text", ".wasm": "binary" }`; built-in handling for `.ts`/`.tsx`/`.js`/`.jsx`/`.json`/`.css` is preserved unless overridden, and longer extensions match first so `".d.ts"` wins over `".ts"`). The accepted values are deliberately narrowed to the portable `BundlerLoader` set (`js`/`jsx`/`ts`/`tsx`/`json`/`css`/`text`/`binary`/`base64`/`dataurl`) — esbuild-specific loaders like `file`/`copy`/`empty`/`default` are intentionally excluded. `file`/`copy` would silently break in this bundler today (they emit secondary output files that get discarded), and anything outside the portable set should go through the plugin escape hatch instead.
- `conditions` (package export conditions, e.g. `["workerd", "worker", "browser"]`)
The first five are re-typed locally (`JsxMode`, `BundlerLoader`) so the published `.d.ts` does not import from `esbuild-wasm` — a future bundler swap is a refactor, not a breaking type change.
For advanced consumers (RSC-style transforms, custom asset pipelines, codegen) there is also an explicit escape hatch:
```ts
__dangerouslyUseEsBuildPluginsDoNotUseOrYouWillBeFired?: unknown[]
```
The deliberately unwieldy name is the API contract: this option is **not** covered by semver, can change shape or be removed in any release, and ties the caller to esbuild's plugin shape — if this package switches bundlers, plugins authored against it will break. It is typed as `unknown[]` at the public boundary (cast `Plugin[]` from `esbuild-wasm` when passing in) so the published types don't acquire a hard dependency on esbuild. User plugins run before the internal virtual-filesystem plugin, so their `onResolve`/`onLoad` claims fire first.
In `createApp`, all of these options apply to both the server and client bundles.
The internal `bundleWithEsbuild` signature was refactored from a long positional argument list to a single options object so future bundler knobs can be added without churning every call site. This is an internal change; no public API moved.
Inspired by [#1321](https://github.com/cloudflare/agents/issues/1321) — thanks @bndkt for the draft and the RSC-on-Workers proof-of-concept that motivated it.
- [#1335](https://github.com/cloudflare/agents/pull/1335) [`e59388d`](https://github.com/cloudflare/agents/commit/e59388d940c780e199cfba7b74d1aaf4d4b471ec) Thanks [@threepointone](https://github.com/threepointone)! - Fix: don't crash with `Cannot find package 'gojs'` when imported from Node.
Previously, `bundler.ts` did a top-level static `import esbuildWasm from "./esbuild.wasm"`. In the Workers runtime that resolves to a `WebAssembly.Module` natively, but in Node 22+ (e.g. Vitest on GitHub Actions CI) Node's experimental ESM-WASM loader actually parses the file and tries to resolve `esbuild-wasm`'s Go-runtime import namespace `gojs` as an npm package. That surfaced as the deeply confusing error reported in [#1306](https://github.com/cloudflare/agents/issues/1306):
Cannot find package 'gojs' imported from
.../@cloudflare/worker-bundler/dist/esbuild.wasm
Two changes:
- The `./esbuild.wasm` import is now lazy — it lives inside `initializeEsbuild()` as a dynamic `import("./esbuild.wasm")` call instead of a module-level static import. The package is now safely importable from any JavaScript runtime.
- Before evaluating that dynamic import, the bundler checks `navigator.userAgent === "Cloudflare-Workers"`. If it's not running inside workerd, it throws an actionable error pointing the caller at `@cloudflare/vitest-pool-workers` instead of letting Node surface the cryptic `gojs` resolution failure.
A side benefit: `createWorker({ bundle: false })` (transform-only mode, which never invokes esbuild) now also works in Node, because the WASM is never loaded on that code path.
The README now also calls out the Workers-only requirement near the top.
While in there, sharpened a handful of unhelpful error messages to include actionable context:
- "Entry point/Server entry point/Client entry point ... not found" now lists the user-provided files in the bundle (skipping `node_modules/`) so it's obvious whether the path is mistyped vs. missing entirely.
- "Could not determine entry point" now spells out the full priority list it tried (`entryPoint` option → wrangler `main` → `package.json` → defaults).
- npm registry errors include the package name, version, registry URL, and HTTP status text — e.g. `Registry returned 404 Not Found for "hno" at https://registry.npmjs.org/hno (package not found — check the name in package.json or set the `registry` option if it lives on a private registry)`.
- The npm fetch-timeout error names the URL and notes the registry was slow/unreachable from the Worker.
- "Invalid package.json" includes both the path and the underlying parse error.
- "No output generated from esbuild" now names the entry point and explains the two real-world causes (a custom plugin claiming the entry without returning contents, or the entry resolving to an externalised module).