Vite 8, Rails 8#607
Open
andriytyurnikov wants to merge 11 commits intoElMassimo:mainfrom
Open
Conversation
- Tighten vite peer dep to >=8.0.0 in vite-plugin-ruby and vite-plugin-rails.
- Bump vitest 0.34 to 4.1 in both plugin packages.
- Bump TypeScript ^5 to ^6, tsdown to ^0.21.10, @types/node ^22 to ^24, rollup ^4.2 to ^4.60, plus minor bumps for obug, tinyglobby, vite-plugin-full-reload, @types/debug.
- Tighten vite-plugin-rails workspace dep on vite-plugin-ruby to ^5.2.1 so the rolldown fix is required.
- Bump vite to ^8 in test/test_app and examples/{hanami_bookshelf,padrino_blog_tutorial}; align hanami engines with current Node LTS.
- Relax examples/rails engines from exact node:22/pnpm:9 to >=22/>=9 so it runs on current Node 24 LTS.
- Refresh build.spec.ts hashes and snapshot for Rolldown chunk output.
- Add gemfiles/Gemfile-rails.8.1.x to the CI matrix; Rails 8.1 is the current stable and examples/rails already locks to 8.1.x. - Bump padrino in examples/padrino_blog_tutorial from 0.15.0 to 0.16.1. - Bump examples/hanami_bookshelf to Ruby 3.4.5 (Gemfile and .ruby-version), matching examples/rails. - Raise required_ruby_version to >= 3.3 across all gemspecs (vite_ruby, vite_rails, vite_hanami, vite_padrino, vite_plugin_legacy, vite_rails_legacy). Ruby 3.2 reached EOL in March 2026.
vite_rails_legacy targets Rails 4 (railties < 5), EOL since 2017. Its actual audience is on legacy Ruby, so the previous commit's bump to 3.3 would orphan them. Restore the original floor.
The example used a TypeScript non-null assertion on process.env.ADMINISTRATOR_ASSETS_PATH that is unset at build time when no Rails engine is wired up, leaving [undefined] in server.fs.allow and an alias resolving to "undefined/...". Vite ≤7 silently tolerated those undefined entries; Vite 8 strictly path-resolves server.fs.allow and crashes with "paths[1] argument must be of type string". Make both the alias and the fs.allow entry conditional on the env var actually being present, so the build works whether or not the engine fixture is wired up.
ruby.yml: - Drop Ruby 3.1 and 3.2 from the stable matrix (gemspec floor is 3.3, bundle install would fail on those). - Add Ruby 3.4 to the stable matrix. - Promote Gemfile-rails.8.0.x to stable; add Gemfile-rails.8.1.x. - Add Ruby 4.0 as experimental against 8.1.x and edge. - Bump setup-node from 20 to 22 (Vite 8 requires Node 20.19+/22.12+). js.yml: - Bump pnpm action-setup from 9 to 10. - Test on Node 22 and 24 instead of just 20. - Run the build + tests for vite-plugin-rails as well as vite-plugin-ruby. - Install at workspace root with --frozen-lockfile so workspace deps resolve. - Pass --run to test step so vitest 4 doesn't enter watch mode.
js.yml: vite-plugin-rails tests resolve workspace vite-plugin-ruby through its built dist/, so the matrix job for rails must also have vite-plugin-ruby built. Replace the per-package build step with pnpm -r build to build every workspace package before tests run. ruby.yml: test/test_app uses yarn.lock and the rake task that exercises yarn install --frozen-lockfile no longer finds yarn on GitHub's Node 22 image. corepack ships with Node and provides yarn/pnpm shims; enable it so the test can install test_app's deps.
The vite ^5 to ^8 bump in test/test_app left yarn.lock stale, so RakeTasksTest's exec of yarn install --frozen-lockfile failed in CI.
- fix(vite-plugin-ruby): return after res.end() in dev /index.html 404 fallback so downstream middleware doesn't write to a closed response. - fix(vite_ruby): guard dev_server_running? cache with a Mutex; the prior read of @running before @running_checked_at was set could raise on a cold concurrent call from a multi-threaded server. - chore(ci): pin qltysh/qlty-action/coverage to v2.2.0 SHA and tighten the upload guard to (push && refs/heads/main). - chore: drop @vitest/coverage-v8 and the JS coverage upload path; the per-package coverage scripts and vitest coverage blocks are removed. - chore(rubocop): bump TargetRubyVersion / standard inherit_gem 3.0 -> 3.3 to match the CI matrix floor. - chore(test): remove dead RUBY_VERSION 2.4 branch in dev_server_proxy_test.
build.spec.ts pinned exact 8-char content hashes (e.g. assets/main-Ddvw3iap.js) in both the file-list assertion and the snapshot. Any routine bump of Vite, Rolldown, Vue, or sass that changes a chunk by even a byte forces a manual hash regeneration even though the manifest shape is unchanged. Strip the 8-char hash from filenames before asserting, and replace the sub-resource integrity value with a `<integrity>` placeholder in the snapshot. The test now fails on structural changes (entry → file mapping, imports list, css list, etc.) but absorbs byte-level churn from upstream dependency updates.
vitest sets NODE_ENV=test by default, and build.spec.ts spawns `npm run build` inheriting that env. Vite respects an externally-set NODE_ENV — it doesn't overwrite it from --mode production — so the test-driven build was producing Vue's *development* bundle (Object.freeze, __file injection with absolute paths, prop validators, dev-only warnings). Local `npm run build` (no vitest in the loop) shipped Vue's production bundle. The two outputs diverged by ~27 KB and produced different chunk hashes — the symptom we previously misdiagnosed as cross-platform drift. Setting NODE_ENV=production in the build script makes the build match production semantics regardless of how it's invoked, so the test exercises the same code real users ship.
ElMassimo#588) The plugin was setting `build.sourcemap = !isLocal`, which inverted Vite's safe default: - production / staging → sourcemap=true (publishes .js.map publicly, exposing application source code) - development / test → sourcemap=false (breaks debugging where you'd actually want sourcemaps) Drop the override entirely. Vite's default (`false`) now applies in every mode, and consumers opt in to sourcemaps via `build.sourcemap` in their own vite.config when they actually want them — for production via external upload to e.g. Sentry, or for development debugging. The unit test in vite-plugin-ruby/tests/index.spec.ts had been encoding the inverted behavior; updated to assert `sourcemap` is no longer set by the plugin. The integration test in vite-plugin-rails drops the `.map` entries from the expected file list since the example build no longer emits them.
This was referenced Apr 27, 2026
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.
Summary
Brings vite_ruby and the workspace plugins into compatibility with Vite 8 (Rolldown-powered) and refreshes the CI matrix and Ruby framework targets. Also fixes two latent runtime bugs, a sourcemap-in-production security default, and a test-determinism issue surfaced during the upgrade.
Vite 8 compatibility
vite-plugin-rubyandvite-plugin-railspeer-depvite >=8.0.0.examples/rails: gate the@administrator/alias andserver.fs.allowentries onADMINISTRATOR_ASSETS_PATHactually being set. The previous code used a TypeScript non-null assertion (process.env.X!), so when the env var was unset,server.fs.allowreceived[undefined]. Vite ≤7 silently dropped non-string entries; Vite 8 strict-validates them and crashes withTypeError [ERR_INVALID_ARG_TYPE]: paths[1] must be of type string.Bug fixes
vite-plugin-ruby: stop forcingbuild.sourcemap = !isLocal. The inverted default was publishing.js.mapfiles in production (exposing application source) and disabling them in development (breaking debugging). Drop the override; Vite's safe default offalsenow applies in every mode, and consumers opt in via their ownbuild.sourcemapconfig when they actually want sourcemaps.vite-plugin-rubydev middleware:returnafterres.end()in the/index.html404 fallback so downstream middleware doesn't write to a closed response.vite_ruby: synchronizedev_server_running?cache with aMutex. The prior read of@runningcould race with the write of@running_checked_atand raiseNoMethodErroron a cold concurrent call from a multi-threaded server (Puma).Test determinism
vite-plugin-rails/example's build script to pinNODE_ENV=production. Without it, vitest's defaultNODE_ENV=testleaked into the test-driven build, shipping Vue's development bundle (withObject.freeze, prop validators, and@vitejs/plugin-vue's absolute-path__fileinjection — ~27 KB extra and per-machine paths).build.spec.ts: strip 8-char content hashes and replace SRI values with a<integrity>placeholder so the snapshot survives routine dep bumps that change chunk bytes without changing manifest shape.CI / tooling
bundle installwould fail).setup-nodeto 22 (Vite 8 requires Node 20.19+/22.12+).vite-plugin-rubyandvite-plugin-rails, build all workspace packages, install at workspace root.corepackso the test_app's yarn-driven setup findsyarn.test/test_app/yarn.lockfor the Vite 8 bump.Related (likely resolved as side effects)
'vite' does not provide an export named 'defaultClientConditions'. Tightening the peer-dep tovite >=8and pinningDEFAULT_VITE_VERSION = "^8.0.0"sidesteps the mixed-version state that triggered the import error.vite-plugin-ruby ^5.2.0(whichDEFAULT_PLUGIN_VERSIONalready targets onmain).bundledDevmode under Vite 8". This PR doesn't wire bundledDev itself but is the prerequisite for that work.Test plan
pnpm -r build— both workspace packages build clean.pnpm -C vite-plugin-ruby test --runandpnpm -C vite-plugin-rails test --runpass.examples/railsvite buildsucceeds withADMINISTRATOR_ASSETS_PATHpointing atexample_engine/app/frontend.