Skip to content

Client bundle crashes at boot (ReferenceError: process is not defined) when ripple.config.ts imports defineConfig from @ripple-ts/vite-plugin, silently breaking all interactivity #1422

Description

@chenzylab

Summary

ripple.config.ts gets bundled into the client build because the generated hydration entry (create_client_entry_source in @ripple-ts/vite-plugin) does import rippleConfig from '<configPath>' directly. Since a typical ripple.config.ts imports defineConfig from @ripple-ts/vite-plugin, the whole plugin module ends up in the client bundle too — including its module-level const IS_WINDOWS = process.platform === 'win32'; (packages/vite-plugin/src/index.js:48).

In the browser this throws ReferenceError: process is not defined at the top level of the client entry chunk. Because it's a top-level throw (not inside the try/catch that wraps hydrate()/mount()), it aborts the entire client bootstrap script before hydrate() ever runs. The result: SSR output looks correct, no console error about hydration mismatch, but every interactive component on the page is inert — click handlers never get attached, and there's no error surfaced that points at the real cause.

Reproduction

  1. create-ripple app (or any app) using @ripple-ts/vite-plugin's RenderRoute, with ripple.config.ts importing defineConfig from @ripple-ts/vite-plugin.
  2. Add any component with a click handler (e.g. a toggle/switch) to a render route page.
  3. vite build for the client, then serve the built output (node dist/server/entry.js with the adapter-node/bun/vercel entry, or preview).
  4. Open the page in a browser and click the interactive element.

Observed: nothing happens. DevTools console shows:

ReferenceError: process is not defined
    at .../assets/main-<hash>.js:<line>

pointing into the bundled @ripple-ts/vite-plugin code, not into any application code.

This reproduces identically in dev mode and in a production build — it is not an optimizeDeps/dep-scan artifact. (There's a related, separate dev-only noise issue where @tsrx/core's vite/dep-scan.js — which imports node:fs/promises — gets pulled into the client dep-scan; that one only affects the dev console and does not break interactivity, so it's not the same bug, just easy to conflate since both surface as "Node module in browser" errors.)

Root cause

packages/vite-plugin/src/index.js:48:

const IS_WINDOWS = process.platform === 'win32';

This is evaluated eagerly at module load, with no guard. IS_WINDOWS is only read inside createVirtualImportId (line ~132), a server-only helper for building Vite virtual module ids from Windows-style /@fs/C:/... paths — it has no reason to exist in a client bundle at all, but it still gets executed because the whole module is imported.

Suggested minimal fix

Guard the top-level access so an accidental client-side import doesn't crash the whole script:

const IS_WINDOWS = typeof process !== 'undefined' && process.platform === 'win32';

This alone would prevent the hard crash, though the deeper issue is that create_client_entry_source importing the whole ripple.config.ts (transitively pulling in @ripple-ts/vite-plugin, and whatever else the config file imports) into the client graph seems unintentional — SERVER_ONLY_ADAPTER_IDS already stubs out @ripple-ts/adapter-node/-bun/-vercel for exactly this reason, but @ripple-ts/vite-plugin itself isn't in that list. A more complete fix might extract only the route table from the config at build time rather than importing the live config module client-side, but I wanted to report the observable symptom first since it's a pretty severe one (silently breaks all interactivity with no actionable error).

Environment

  • ripple / @ripple-ts/vite-plugin / @ripple-ts/adapter-node: 0.3.119
  • Reproduced via SSR render routes (RenderRoute + Node adapter), both vite dev and production build (vite build + node dist/server/entry.js)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions