You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fixed
A dependency in a node_modules above the app root (a workspace's hoisted install) is served however its request arrives. oj allowed such a package only once a module importing it had compiled, so a page reloading into a restarted dev server, which fetches the imports it remembers before their importers, got a 403 for react-dom's scheduler and rendered nothing. Every node_modules on the root's lookup path is now allowed from boot, as Vite serves what its resolver resolves.
The TanStack Start dev bundler holds less memory between rebundles. Each build's outputs kept their code in rolldown's native memory until V8 collected their JS wrappers, and a bundler idling until the next edit allocates nothing, so V8 never did. The outputs are now freed with rolldown's freeExternalMemory once they are written: on a 3,000-module app on Linux, the idle bundler drops from about 570MB to about 455MB.
A bare import nothing resolves (import x from "not-installed-pkg") fails the importer with Vite's Failed to resolve import again in apps with no plugin resolveId. Since the plugin host runs for every app, oj took its mere presence to mean a plugin might serve the specifier and shipped a /@id/ url instead. Now only a plugin with a resolveId hook gets that chance, as in Vite's importAnalysis.
Paths under /__ belong to the app, as in Vite, which reserves no such prefix. TanStack Start and SSR dev handed every /__* request to the dev server's own routes, so an app route like a component preview (/__component/preview/$) never reached SSR and answered "oj: no such file" whenever no plugin middleware served it; the SPA fallback skipped /__* navigations the same way. Only oj's own endpoints (/__ws, /__hmr_flush, /__hmr_gate, and SSR dev's /__oj_fn) stay the dev server's.
The dev server (plain, SSR and TanStack Start) binds its port only once the file watcher covers the root, as Vite creates its watcher before listening. A file written the moment the server first answered could go unseen, a .env edit right after a restart included.
A slow or hung plugin hot-update hook no longer holds every later file change back. The dev watcher ran each batch's HMR (which awaits the plugin host for its watch files and handleHotUpdate, as long as host init or the hook takes) on its own thread, so a .env or config edit behind it never restarted the server. Each batch's HMR now runs as its own task, as Vite handles each watcher event on its own, so a hung hook parks only its own file's update: other files still hot-update and config and .env edits restart right away. The restart line names the file that changed, as Vite's does.
Start dev rebundles pick up an edited CSS module or preprocessor sheet again. The client bundler now lives for the whole session, and its engine kept each stylesheet compile cached by url, so after an edit the client bundle kept the old class names while SSR rendered the new ones. Style imports now carry an epoch that moves whenever a stylesheet, partial or PostCSS/Tailwind config changes.
Dev pages go through Vite's whole dev HTML transform. An inline <script type="module"> is lifted into an html-proxy module (/index.html?html-proxy&index=0.js, or /@id/__x00__/<page>?html-proxy&index=0.js for a page with no file) whose imports resolve like any module's, so a bare import React from "react" in a page reaches the optimized dep instead of the browser. The same chain backs server.transformIndexHtml, which ran only the plugins' hooks, ignored its url and injected no dev client, so an HTML page a configureServer middleware rendered through it shipped unresolvable imports. Hooks run in Vite's order (import map, CSP meta, pre, %ENV%, the dev transform, normal, post, nonces), tags inject after each hook the way Vite's do (<head lang> included), script and asset urls are rebased like Vite's (a SPA fallback on /a/b points ./src/main.tsx at /src/main.tsx), and a /@id/ url keeps its query as part of the id.
An inline <style> in a dev page compiles like a stylesheet beside the page, as Vite runs it through its CSS pipeline: plugin transforms, PostCSS and Tailwind directives, @import inlining and aliased or relative url()s. A style attribute holding a url() or image-set() goes through the app's PostCSS plugins and then gets its urls rewritten as text, as Vite's default PostCSS transformer does, so the declarations stay as written. oj shipped both as written, so a page <style> using @apply or importing a package rendered unstyled.
Plugin transforms see a dev page's inline <style> and style attributes under Vite's ids (/abs/index.html?html-proxy&direct&index=0.css, ...&inline-css&style-attr&index=1.css); attributes skipped plugin transforms entirely, and a <style> reached them as index.html.css. An app with no active plugins (or no config) now gets the dev HTML transform too, as under Vite: its plugin host stays up, idle, instead of shutting down, and pages served as written before.
An image-set() candidate in a dev stylesheet prints as url("..."), as Vite's url rewrite writes it, instead of a bare string.
A relative url() in a dev stylesheet is rewritten only when it names a file, as Vite's dev url resolver does: one naming no file stays as written instead of being pointed at a server path. That includes every relative url in an inline style of a page with no file (one a middleware renders), which Vite resolves from no directory at all.
TanStack Start dev keeps one client plugin container for the whole session, as Vite keeps one per server, so a plugin's buildStart runs once and its state survives rebundles. Each rebundle used to load the app's plugins fresh, so a plugin that writes a generated module from buildStart whenever its in-memory copy differs (the usual codegen shape) rewrote that file on every bundle, the watcher fed the write back as the next rebundle, and the page reloaded about once a second forever. The client bundler is now one long-lived child per dev server; plugin buildEnd runs when the session ends, as in Vite dev.
Dev pages go through Vite's whole dev HTML transform. An inline <script type="module"> is lifted into an html-proxy module (/index.html?html-proxy&index=0.js, or /@id/__x00__/<page>?html-proxy&index=0.js for a page with no file) whose imports resolve like any module's, so a bare import React from "react" in a page reaches the optimized dep instead of the browser. The same chain backs server.transformIndexHtml, which ran only the plugins' hooks, ignored its url and injected no dev client, so an HTML page a configureServer middleware rendered through it shipped unresolvable imports. Hooks run in Vite's order (import map, CSP meta, pre, %ENV%, the dev transform, normal, post, nonces), tags inject after each hook the way Vite's do (<head lang> included), script and asset urls are rebased like Vite's (a SPA fallback on /a/b points ./src/main.tsx at /src/main.tsx), and a /@id/ url keeps its query as part of the id. Inline <style> and style attribute urls are not transformed yet.
A file no module graph knows no longer gets read when it changes. The plugin host hashed every changed file's full contents to drop oj's duplicate notification for one edit, so a database a local worker rewrites on every request (Miniflare's trace store under .wrangler/state, which grows to gigabytes) was read whole on each write: memory spiked by the file's size and the host stalled, leaving every page request hanging. Such files are now told apart by their stat, as Vite never reads a changed file unless a plugin asks; module files still compare by content.