Repository navigation
[rsbuild-plugin] Prototype an Rsbuild plugin that runs Workers in workerd - #16362
Draft
james-elicx wants to merge 5 commits into
Draft
james-elicx wants to merge 5 commits into
james-elicx wants to merge 5 commits into
Conversation
🦋 Changeset detectedLatest commit: 7bc7ad7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
…kerd Adds a private `@cloudflare/rsbuild-plugin` prototype that builds the Worker from `cloudflare.config.ts` with rspack, runs it in Miniflare during `rsbuild dev` and `rsbuild preview`, and writes the Build Output Specification on `rsbuild build`. Logic the Vite plugin already had is moved into a private `@cloudflare/workers-dev-shared` package so both plugins use it: config loading, local bindings, Node.js compat, Miniflare exports, persistence and Build Output manifests.
…playground e2es Move the rest of the Vite plugin's bundler-agnostic dev, preview and build logic into `@cloudflare/workers-dev-shared`, so the Rsbuild plugin gets the same Miniflare options, middlewares, asset handling, multi-Worker support, remote bindings, additional modules and Build Output as the Vite plugin. Run the Vite plugin's playground suite against the Rsbuild plugin without changing the specs: a harness translates each playground's Vite config, and Vite-specific playgrounds and tests are skipped with their reasons.
james-elicx
force-pushed
the
t3/rspack-rsbuild-workerd-plugin-poc
branch
from
October 19, 2026 19:33
2399268 to
cb496a4
Compare
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/codemods
@cloudflare/config
@cloudflare/containers-shared
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/runtime-types
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-plugin
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
…he .dev.vars fixture The Vite playground jobs build only the Vite plugin, so importing the Rsbuild harness eagerly failed every spec. The root .gitignore excludes .dev.vars, so the Rsbuild fixture's secret was missing in CI.
Move the per-Worker part of the Vite plugin's dev Miniflare options into `getDevUserWorkerOptions` in `@cloudflare/workers-dev-shared`. The Vite plugin extends it with its module-runner bindings. The Rsbuild plugin uses it as-is, so it now also enables headful browser rendering when requested, and like the Vite plugin it turns off Miniflare's request logging. Also stop rebuilding the whole Worker environment config just to read its entry, and tidy the entry Worker middleware setup.
seed() converts each fixture to an Rsbuild project when CLOUDFLARE_TEST_BUNDLER=rsbuild, installing a packed copy of the private plugin. Fixes two gaps the suite found: Worker-only projects didn't print the dev server URL, and an unresolvable entrypoint didn't fail at startup.
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.
Fixes #[insert GH or internal issue link(s)].
A proof of concept for an Rsbuild/rspack equivalent of
@cloudflare/vite-plugin. The Rsbuild plugin and the shared package are private, so nothing new is published.The goal is a thin Rsbuild wrapper over the Vite plugin's existing behaviour. Bundler-agnostic code is moved out of the Vite plugin into the private
@cloudflare/workers-dev-sharedpackage, which both plugins bundle. The Rsbuild plugin keeps only the Rsbuild glue: hooks, environment config, rspack externals/plugins and its middleware order. It's about 1,070 lines (packages/rsbuild-plugin-cloudflare/src).What it does
@cloudflare/rsbuild-plugintakes the same options ascloudflare()(excepttunnel/viteEnvironment). Each Worker gets its own Rsbuild environment (cloudflarefor the entry Worker,cloudflare_<directory>for auxiliary Workers), and the user's environments are the client.rsbuild dev: after each compile the Workers are loaded into Miniflare with the same options as the Vite plugin: router/asset/proxy Workers,_headers/_redirects, static routing, WebSockets, the inspector and/__debug, cron/email triggers, remote bindings, the local explorer, and the dev registry. HTML navigations go through the asset Worker, sohtml_handling/not_found_handlingapply as in Vite.rsbuild build: writes Cloudflare Build Output for every Worker, including assets-only/dev-only Workers,public/and assets imported by the Worker.rsbuild preview: runs the Build Output with the shared preview options.nodejs_als), additional modules (.wasm,?init,.bin,.txt,.html,.sql),cloudflare:*externals and Worker sourcemaps assourcemapmodules. Resolution uses the same conditions and main fields as the Vite plugin.Moved from the Vite plugin into
@cloudflare/workers-dev-sharedMoved, not copied. The Vite plugin's
srcdiff is +278/−4,400 lines, and the specs for the moved code moved with it:resolvePluginConfig's core), env loading, asset configgetDevUserWorkerOptions), the Miniflare logger, remote bindings/__debugmoveImportedAssets.wasm?initmodule,hasNodeJsAls/isNodeAlsModuleChanges to existing logic in the Vite plugin
The aim was no behaviour change. These are the only deviations I found when auditing the diff:
api: { pluginConfig }(the options passed tocloudflare()), so the e2e harness can read them from a resolved Vite config. Additive; patch changeset included.vite previewnow reads the Build Output before validating the tunnel config (allowedMailwithoutname). With both problems present, the Build Output error is reported first.__VITE_HEADERS__is nowJSON.stringify(headers ?? {}). Vite 8 always setsserver.headersto{}by default, so there's no difference there; I haven't checked whether Vite 7 can leave it undefined.dts.resolve) and keepminiflare/@cloudflare/configas imports. The d.ts imports only@cloudflare/config,miniflareandvite. The@cloudflare/workers-utilsimport is gone because the types that needed it moved.src/utils.tswere removed (they moved). They weren't public.getDevUserWorkerOptions, which the Vite plugin extends with its module-runner bindings, exports and dev options. The options are the same. The only difference is the order of two internal assertions, which would only fail on a bug.Everything else is a verbatim move plus import updates.
Running the playground e2es against both plugins
The existing playground suite runs against Rsbuild without spec changes:
playground/__test-utils__/rsbuild.tsloads each playground'svite.config, readsapi.pluginConfig, and translates a small set of Vite options:root,base,server.headers,resolve.alias,mode/envDir, HTML inputs, the SSR build'ssourcemap/entryFileNames, andplugin-react/basic-ssl. It throws on Vite plugins it can't translate.vitest-setup.tsbranches onCLOUDFLARE_TEST_BUNDLER=rsbuild.playground/vitest.config.e2e.rsbuild.tsskips Vite-specific playgrounds and tests, each with the reason.test:ci:rsbuild:serve/test:ci:rsbuild:buildwith turbo tasks, plus a separate ubuntu-only CI job. It runs without--only, so turbo builds the plugins and keys the cache on them.Locally: Rsbuild serve and build pass on everything that isn't skipped (73 spec files, of which serve runs 71 and build 68; the rest skip themselves with
runIf), and the full Vite suite still passes (96 files, serve and build).Running the Vite plugin's
e2e/suite against both pluginspnpm turbo test:e2e:rsbuild -F @cloudflare/vite-pluginrunspackages/vite-plugin-cloudflare/e2eagainst Rsbuild, also without spec changes. CI runs it as an extra step of the Linux leg of "Vite Plugin E2E".e2e/rsbuild.tspacks the private Rsbuild plugin in a global setup. Its dependencies are the Vite plugin's, which the mock registry already publishes. It also converts each seeded fixture:@cloudflare/vite-plugin/vite/@vitejs/plugin-reactbecome@cloudflare/rsbuild-plugin(the packed tarball),@rsbuild/coreand@rsbuild/plugin-react;vitescripts becomersbuildones;vite.configbecomesrsbuild.config, with thecloudflare()options passed unchanged topluginCloudflare().seed()calls it whenCLOUDFLARE_TEST_BUNDLER=rsbuild. That one line is the only change to the existing helpers.e2e/vitest.config.rsbuild.tsskipsdynamic(Vite's dependency optimizer),invalid-worker-environment-options(Vite environment validation),nodejs-compat-warnings(asserts the "ssr" environment name) andcontainers-image-free.Locally,
basic(pnpm/npm/yarn × dev/build+preview),unresolved-mainand the remote-bindings tests that don't need credentials all pass. The suite found two Rsbuild plugin gaps, now fixed, and one difference that remains:rsbuild devdidn't print the server URL for Worker-only projects, because Rsbuild only prints it for client environments.rsbuild devkept running. It now fails at startup withFailed to resolve Worker entrypoint "…", as in Vite..env.[NODE_ENV]files intoprocess.env, where they override the plugin's.env.[mode]files. So withNODE_ENV=test(which Vitest sets and the commands inherit), the values differ from Vite's. The suite clearsNODE_ENV; the plugin can't see which variables Rsbuild loaded, so this difference stays.Skipped for Rsbuild (playgrounds)
buildApp, middleware mode,bundledDev, child compilers, the error overlay (errors), prerendering,module-resolution,resolve.external,partyserver, custom output directories.server.fs.deny403s (sensitive-files), HMR log strings, not serving HTML frompublicDirpaths inside the root.preserveModules(dynamic-import-paths).config-changes: it evaluatescrypto.randomUUID()at global scope, which only works because Vite's module runner evaluates lazily.usingdeclarations: the Worker runs them, but the output-shape assertion is skipped.Not supported yet
unsafeHandleRuntimeRestartcfCLIThings to look at
@cloudflare/workers-dev-sharedis the right home, or whether some of it belongs in Miniflare.api.pluginConfigfor tooling.Note
This is a contribution from an AI agent: Claude Code, Claude Opus 5.5. It was written under the direction of @james-elicx, who will review it.