@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).