Add @arkenv/rsbuild-plugin for TanStack Start + Rsbuild server/client env - #1802
Conversation
🦋 Changeset detectedLatest commit: 2f0b6b7 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 |
@arkenv/agent-plugin
arkenv
@arkenv/build
@arkenv/bun-plugin
@arkenv/core
@arkenv/fumadocs-ui
@arkenv/nextjs
@arkenv/nuxt
@arkenv/rsbuild-plugin
@arkenv/standard
@arkenv/vite-plugin
commit: |
There was a problem hiding this comment.
ℹ️ No critical issues — three minor items: two inline cleanup suggestions plus one coverage note below.
Reviewed changes — Full first review of the new @arkenv/rsbuild-plugin package for TanStack Start + Rsbuild (client rewrite / server passthrough / build-time validation / dev reload). Reviewed the raw diff against the installing @rsbuild/core@2.2.3 typings and docs, and ran the package build + test suite (pnpm --filter @arkenv/rsbuild-plugin... build then vitest, from both the package dir and the root --project): all 15 tests pass, including the two real dual-environment rsbuild.build() integration cases.
- New package scaffold —
.changeset,package.json(deps/peers mirror vite-plugin),README,tsdown/turbo/tsconfig/vitestconfigs,.gitignore. env-module.ts— re-export of the shared@arkenv/buildhelpers (generateClientEnvModule,classifyEnvKeys,loadValidatedEnv,isEnvModuleId,resolveEnvModulePath, transform-mode guards); no duplicated validate/filter/inject logic.transform-plugin.ts—api.transformrewrite scoped to["web", "web-worker"]withorder: "pre", schema +.env*watch registration viaaddDependency/addMissingDependency,onBeforeEnvironmentCompilevalidation that fails the build before assets are emitted,nodetargets left untouched.standard.ts+ factory —arkenvPlugin/arkenvRsbuildPlugin/ default plus a/standardentry; transform-only, schema/definesignature rejected viaassertTransformModeCall.- Tests — transform-helper units, transform descriptor shape, dependency-registration units, and two integration builds pinning the security contract: web bundle inlines public/shared literals and throws on server keys (no
@arkenv/core/arktype/secret strings), node bundle preserves rawenv.tsdefaults, and a missing required variable rejects the build with no emitted assets. These are real assertions against actual built output, not theatre. - Lockfile — adds
@rsbuild/core@2.2.3/@rspack/core@2.2.2; the@emnapi/*1.10→1.11.3 and oxc/@tanstack/react-startre-resolution across unrelated importers is benign ecosystem consolidation.
ℹ️ Dev-reload behavior is unit-asserted but not integration-tested
The headline dev-reload flow — edit a .env* file or the schema during rsbuild dev, rebuild, and see freshly inlined client values — is only exercised at the unit level (asserting addDependency / addMissingDependency were called and onBeforeEnvironmentCompile doesn't throw). The ordering contract between the refresh hook and the transform loader (fresh state.transformedSource before the loader runs on a dirty module) has no live watch/rebuild test, so a regression there would pass CI.
Technical details
# Dev-reload integration coverage
## Affected sites
- packages/rsbuild-plugin/src/transform-plugin.ts — the `api.onBeforeEnvironmentCompile` + `api.transform` handler ordering is the whole dev-reload contract
- packages/rsbuild-plugin/src/env-module.test.ts — "scopes the rewrite to web targets and registers watch dependencies" asserts the wiring only
## Required outcome
- A watch-mode integration test (or an explicit note that it's a followed-up-in) that mutates `.env`/the schema between compiles and asserts the next web bundle contains the refreshed inlined literal.
## Open questions for the human
- Worth a `devServer`/watch integration test in this PR, or acceptable as a follow-up given the unit assertions mirror the vite-plugin pattern?DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
Addressed Review Feedback
|
There was a problem hiding this comment.
ℹ️ No critical issues — prior feedback is all addressed; one minor test-robustness nitpick inline.
Reviewed changes — Re-review of commit d04d5bdd (delta against the prior pullfrog review at 46571db5). The vestigial compatiblePackages block and the dead vitest arkenv/arkenv/arktype aliases were dropped, the refresh-state unit test now actually exercises the transform handler across a .env.test rewrite, and a new live watch-mode integration test covers the dev-reload contract. Verified locally: build + all 16 tests green, and scripts/standard-isolation.test.js green with @arkenv/rsbuild-plugin added.
- Config cleanup — removed the vestigial
compatiblePackagesmetadata block frompackage.jsonand the unusedarkenv/arkenv/arktypevitest aliases (plus the now-unusednode:pathimport). - Dev-reload integration coverage — added a real
rsbuild.build({ watch: true })test that rewrites.env.test, waits for the rebuild, and asserts the refreshed inline literal ships in the next bundle; the state-refresh unit test now drives the before-compile hook through the transform handler and asserts refreshed output. - Standard-isolation coverage —
scripts/standard-isolation.test.jsnow includespackages/rsbuild-plugin. - Test robustness —
collectJstolerates a missingdistdir (returns"").
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
@pullfrog review |
There was a problem hiding this comment.
ℹ️ No critical issues — prior feedback is fully addressed; one defer-able question inline about client-rewrite target scope.
Reviewed changes — Re-review of commit ab485f4 (delta against the prior pullfrog review at d04d5bdd). The watch-mode dev-reload integration test now wraps its build/assertions in a try/finally so await close() runs even when an assertion fails, so a failing test can't leak the rsbuild watcher. The fix is correct and resolves the prior open thread at env-module.test.ts:424.
- Test robustness —
close()moved into afinallyblock; the rest of the plugin (shared@arkenv/buildhelpers,api.transformweb/node split,onBeforeEnvironmentCompilevalidation, env-source precedence) re-verified against the published@rsbuild/core@2.2.3types/source and CI.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
@pullfrog review |
…-plugin-for-tanstack-start-+-rsbuild-serverclient-env # Conflicts: # pnpm-lock.yaml
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — Re-review of @arkenv/rsbuild-plugin at the head, which adds only the origin/v1 sync merge over the last-reviewed commit (ab485f4). Read the full PR diff end-to-end and verified locally on 2f0b6b75: workspace build, all 16 plugin tests (including the dual-environment rsbuild.build() integration cases and the watch-mode dev-reload test), tsc --noEmit, and scripts/standard-isolation.test.js (4 tests) are green; the head's CI check runs are green as well.
- V1 sync merge — Merges
origin/v1into the branch; touches only unrelated package-version bumps, the.changeset/pre/pre-release migration, andapps/wwwchanges. Nothing underpackages/rsbuild-pluginchanged, and the TanStack Start docs still describe only the Vite path — the "no Rsbuild parity claim until example + guide land" acceptance criterion from #1794 holds. - Prior feedback all resolved — The dead
arkenv/arkenv/arktypevitest aliases, the vestigialcompatiblePackagesblock, and the watcherclose()leak are fixed; theservice-workertarget-scope thread was answered directly (@rsbuild/core'sRsbuildTargetonly supportsweb/node/web-worker, sotargets: ["web", "web-worker"]already spans every browser-context target). All four threads are resolved. - New package —
@arkenv/rsbuild-pluginconsumes shared@arkenv/buildhelpers (classifyEnvKeys,generateClientEnvModule,loadValidatedEnv, schema discovery) with no duplicated validate/filter/inject logic, ships a/standardentry, and rejects the removed schema/definesignature viaassertTransformModeCall. - Client rewrite / server passthrough —
api.transform(order: "pre") scrubs onlyweb/web-workertargets,nodekeeps rawenv.ts, validation inonBeforeEnvironmentCompilefails the build before assets emit, and schema +.env*files are registered as build dependencies for dev reload. - Tests — Transform-helper units plus real dual-environment builds pinning the security contract (public/shared values inlined as coerced literals, zero server secrets in the web bundle, throwing
DATABASE_URLgetter, node bundle preserves schema defaults) and a missing-required-var case that rejects with no emitted assets; the watch-mode test asserts refreshed inlined literals after a.envrewrite.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to v1, this PR will be updated.⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ `v1` is currently in **pre mode** so this branch has prereleases rather than normal releases. If you want to exit prereleases, run `changeset pre exit` on `v1`.⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ # Releases ## @arkenv/rsbuild-plugin@1.0.0-alpha.1 ### Minor Changes - #### Add `@arkenv/rsbuild-plugin` for Rsbuild and TanStack Start _[`#1802`](#1802) [`b13f263`](b13f263) [@yamcodes](https://github.com/yamcodes)_ The new `@arkenv/rsbuild-plugin` brings the same server/client env path as `@arkenv/vite-plugin` to Rsbuild projects, including TanStack Start apps built with Rsbuild: - **Client rewrite**: in `web` and `web-worker` environments, the `env.ts` module is replaced with a scrubbed client module — public/shared keys (default prefix `PUBLIC_`) are inlined as coerced literals and server-only keys become throwing getters, so secrets never reach the browser bundle. - **Server passthrough**: in `node` environments the real `env.ts` runs unchanged against the deployment environment. - **Build-time validation**: the schema is validated via `@arkenv/build` before each environment compiles; missing or invalid required variables fail the build before assets are emitted. - **Dev reload**: the schema and `.env*` files are registered as build dependencies, so edits re-validate and refresh inlined values during `rsbuild dev`. - **Standard Schema**: an `@arkenv/rsbuild-plugin/standard` entry mirrors the ArkType-free path of `@arkenv/vite-plugin/standard`. Usage: ```ts // rsbuild.config.ts import { defineConfig } from "@rsbuild/core"; import { arkenvPlugin } from "@arkenv/rsbuild-plugin"; export default defineConfig({ plugins: [arkenvPlugin({ schemaPath: "src/env.ts" })], }); ``` Install with `npm install @arkenv/rsbuild-plugin arktype`. Co-authored-by: arkenv-bot[bot] <237618717+arkenv-bot[bot]@users.noreply.github.com>

Fixes #1794
Ships
@arkenv/rsbuild-pluginon the v1 branch, giving TanStack Start + Rsbuild the same server/client env path that@arkenv/vite-pluginprovides for Vite:web/web-workerenvironments theenv.tsmodule is replaced viaapi.transformwith a scrubbed client module —PUBLIC_keys inlined as coerced literals, server-only keys replaced with throwing getters (Do not access server-only key '…' on the client…), validator imports stripped.nodeenvironments keep the realenv.tsso boot-time validation runs against the deployment environment.loadValidatedEnvruns inonBeforeEnvironmentCompileand fails the build before assets are emitted..env*files are registered as build dependencies viaaddDependency/addMissingDependency, so edits re-validate and refresh inlined values duringrsbuild dev./standardentry mirroring@arkenv/vite-plugin/standard.Shared logic (
generateClientEnvModule,classifyEnvKeys,loadValidatedEnv, schema discovery) comes from@arkenv/build— no duplicated validate/filter/inject logic.Tests mirror the vite-plugin transform/scrub cases plus dual-environment isolation: a real
rsbuild.build()withweb+nodeenvironments asserts the client bundle inlines public values and contains zero server secrets, while the node bundle preservesenv.tsuntouched; a missing required variable fails the build with no emitted assets.Out of scope (per the issue): docs claiming Rsbuild parity, an example app, and the CLI init scaffolder.