fix: serve loader responses as CJS for ?platform=native requests - #755
Closed
clayrisser wants to merge 1 commit into
Closed
fix: serve loader responses as CJS for ?platform=native requests#755clayrisser wants to merge 1 commit into
clayrisser wants to merge 1 commit into
Conversation
In dev, useLoader on native fetches route loaders with ?platform=native
and runs the response as CJS on hermes. Two gates only recognized
platform=ios|android, so platform=native requests could receive the
vite-transformed web ESM module instead - hermes fails to parse it, the
client swallows the error, and the route silently loses its loader data
(including redirect/auth signals):
- resolveLoaderRoute's isNativeRequest missed platform=native, emitting
redirect (302) and auth (401/403) fallback loader bodies as web ESM
- handleLoader's "export function loader()" stub regex early-returned
the web ESM module above the platform check, so any loader shape the
client tree-shake plugin doesn't rewrite to the literal stub (e.g.
export { x as loader } from './loaders') sent web ESM to native; the
native branch never uses transformedJS at all - it imports the route
through the module runner, which resolves re-exports fine
Web requests keep the existing shortcut (including the skip-SSR-import
optimization for routes without loader stubs).
Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Symptom
In dev,
useLoaderon native fetches route loaders with?platform=native(the comment inuseLoader.tseven says "?platform=nativetells vite plugin to return CJS") and evaluates the response body as CJS on Hermes. But two gates in the request pipeline only recognizeplatform=ios|android, soplatform=nativerequests can receive the Vite-transformed web ESM module instead. Hermes then throws while compiling the loader body:The error is caught and swallowed (
data: {}), so the route silently loses its server-side loader data — including__oneRedirect/__oneErrorsignals, meaning server redirects thrown from loaders never reach the native client.Root cause (two spots)
createHandleRequest.ts→resolveLoaderRoute:isNativeRequestmissesplatform=native, so the CJS conversion (toCjsLoader) and the redirect (302) / auth (401/403) fallback loader bodies are emitted as web ESM for those requests. (The dispatch-level check inhandleRequestalready recognizesnative; this inner one didn't.)fileSystemRouterPlugin.tsx→handleLoader: theexport function loader()stub-regex gate early-returns the raw Vite-transformed web ESM (HMR preamble included) above the platform check that would have produced the native CJS body. Any route whose loader the client tree-shake plugin doesn't rewrite to the literal stub — e.g. a re-export likeexport { pokemonListLoader as loader } from './loaders'— fails the regex and sends web ESM to native. The regex is irrelevant for native responses: the native branch never usestransformedJSat all — it imports the route through the module runner (which resolves re-exports fine) and returns the JSON-embedding CJS body.Fix
nativeinresolveLoaderRoute'sisNativeRequesthandleLoaderabove the stub-regex gate so native requests always take the module-runner path; web requests keep the existing shortcut (including the skip-SSR-import optimization for routes without loader stubs)Verification
createHandleRequest.test.ts. The twoplatform=nativeones fail before the fix (body arrives asexport function loader(){...}web ESM) and pass after; the web test guards against regressions on the ESM path. Full file: 23/23 pass.one@1.16.5. With routes written as plain re-exports:On device (Expo Go SDK 55 / Hermes): the
[one] native loader errortoast is gone and the route receives the server-prefetched loader data.Made with Cursor