Skip to content

Add @arkenv/rsbuild-plugin for TanStack Start + Rsbuild server/client env - #1802

Merged
yamcodes merged 5 commits into
v1from
1794-feat-arkenvrsbuild-plugin-for-tanstack-start-+-rsbuild-serverclient-env
Sep 5, 2026
Merged

Add @arkenv/rsbuild-plugin for TanStack Start + Rsbuild server/client env#1802
yamcodes merged 5 commits into
v1from
1794-feat-arkenvrsbuild-plugin-for-tanstack-start-+-rsbuild-serverclient-env

Conversation

@yamcodes

@yamcodes yamcodes commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Fixes #1794

Ships @arkenv/rsbuild-plugin on the v1 branch, giving TanStack Start + Rsbuild the same server/client env path that @arkenv/vite-plugin provides for Vite:

  • Client rewrite: in web / web-worker environments the env.ts module is replaced via api.transform with 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.
  • Server passthrough: node environments keep the real env.ts so boot-time validation runs against the deployment environment.
  • Build-time validation: loadValidatedEnv runs in onBeforeEnvironmentCompile and fails the build before assets are emitted.
  • Dev reload: schema + .env* files are registered as build dependencies via addDependency / addMissingDependency, so edits re-validate and refresh inlined values during rsbuild dev.
  • /standard entry 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() with web + node environments asserts the client bundle inlines public values and contains zero server secrets, while the node bundle preserves env.ts untouched; 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.

@yamcodes yamcodes added enhancement New feature or improvement arkenv Changes to the `arkenv` npm package. labels Sep 4, 2026
@changeset-bot

changeset-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2f0b6b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@arkenv/rsbuild-plugin Minor

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

@github-actions github-actions Bot added docs Adds or changes documentation, or acts as documentation in and of itself tests This issue or PR is about adding, removing or changing tests and removed arkenv Changes to the `arkenv` npm package. labels Sep 4, 2026
@pkg-pr-new

pkg-pr-new Bot commented Sep 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@arkenv/agent-plugin

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/agent-plugin@1802

arkenv

npm i https://pkg.pr.new/yamcodes/arkenv@1802

@arkenv/build

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/build@1802

@arkenv/bun-plugin

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/bun-plugin@1802

@arkenv/core

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/core@1802

@arkenv/fumadocs-ui

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/fumadocs-ui@1802

@arkenv/nextjs

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/nextjs@1802

@arkenv/nuxt

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/nuxt@1802

@arkenv/rsbuild-plugin

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/rsbuild-plugin@1802

@arkenv/standard

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/standard@1802

@arkenv/vite-plugin

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/vite-plugin@1802

commit: 2f0b6b7

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ 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/vitest configs, .gitignore.
  • env-module.ts — re-export of the shared @arkenv/build helpers (generateClientEnvModule, classifyEnvKeys, loadValidatedEnv, isEnvModuleId, resolveEnvModulePath, transform-mode guards); no duplicated validate/filter/inject logic.
  • transform-plugin.tsapi.transform rewrite scoped to ["web", "web-worker"] with order: "pre", schema + .env* watch registration via addDependency / addMissingDependency, onBeforeEnvironmentCompile validation that fails the build before assets are emitted, node targets left untouched.
  • standard.ts + factoryarkenvPlugin / arkenvRsbuildPlugin / default plus a /standard entry; transform-only, schema/define signature rejected via assertTransformModeCall.
  • 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 raw env.ts defaults, 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-start re-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?

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread packages/rsbuild-plugin/vitest.config.ts Outdated
Comment thread packages/rsbuild-plugin/package.json Outdated
@yamcodes

yamcodes commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

Addressed Review Feedback

  • vitest aliases: Dropped the unused alias block in packages/rsbuild-plugin/vitest.config.ts along with the unused path import.
  • package.json metadata: Removed the vestigial compatiblePackages block from packages/rsbuild-plugin/package.json.
  • Dev-reload coverage:
    • Added unit verification asserting onBeforeEnvironmentCompile refreshes inlined client values when env files are updated.
    • Added a live watch-mode integration test (rsbuild.build({ watch: true })) confirming that mutating an env file re-triggers compilation and outputs the updated inlined values in the bundle.
  • Standard isolation: Added @arkenv/rsbuild-plugin to scripts/standard-isolation.test.js to guarantee ./standard remains free of arktype and @arkenv/core.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ 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 compatiblePackages metadata block from package.json and the unused arkenv/arkenv/arktype vitest aliases (plus the now-unused node:path import).
  • 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 coveragescripts/standard-isolation.test.js now includes packages/rsbuild-plugin.
  • Test robustnesscollectJs tolerates a missing dist dir (returns "").

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread packages/rsbuild-plugin/src/env-module.test.ts Outdated
@yamcodes

yamcodes commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

@pullfrog review

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ 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 robustnessclose() moved into a finally block; the rest of the plugin (shared @arkenv/build helpers, api.transform web/node split, onBeforeEnvironmentCompile validation, env-source precedence) re-verified against the published @rsbuild/core@2.2.3 types/source and CI.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread packages/rsbuild-plugin/src/transform-plugin.ts
@yamcodes

yamcodes commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

@pullfrog review

…-plugin-for-tanstack-start-+-rsbuild-serverclient-env

# Conflicts:
#	pnpm-lock.yaml

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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/v1 into the branch; touches only unrelated package-version bumps, the .changeset/pre/ pre-release migration, and apps/www changes. Nothing under packages/rsbuild-plugin changed, 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/arktype vitest aliases, the vestigial compatiblePackages block, and the watcher close() leak are fixed; the service-worker target-scope thread was answered directly (@rsbuild/core's RsbuildTarget only supports web/node/web-worker, so targets: ["web", "web-worker"] already spans every browser-context target). All four threads are resolved.
  • New package@arkenv/rsbuild-plugin consumes shared @arkenv/build helpers (classifyEnvKeys, generateClientEnvModule, loadValidatedEnv, schema discovery) with no duplicated validate/filter/inject logic, ships a /standard entry, and rejects the removed schema/define signature via assertTransformModeCall.
  • Client rewrite / server passthroughapi.transform (order: "pre") scrubs only web/web-worker targets, node keeps raw env.ts, validation in onBeforeEnvironmentCompile fails 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_URL getter, 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 .env rewrite.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@yamcodes
yamcodes merged commit b13f263 into v1 Sep 5, 2026
19 checks passed
@yamcodes
yamcodes deleted the 1794-feat-arkenvrsbuild-plugin-for-tanstack-start-+-rsbuild-serverclient-env branch September 5, 2026 03:41
@arkenv-bot arkenv-bot Bot mentioned this pull request Sep 5, 2026
yamcodes pushed a commit that referenced this pull request Sep 5, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Adds or changes documentation, or acts as documentation in and of itself enhancement New feature or improvement tests This issue or PR is about adding, removing or changing tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant