fix: fail loudly when client code imports server-only entry points#2207
Open
brenelz wants to merge 1 commit into
Open
fix: fail loudly when client code imports server-only entry points#2207brenelz wants to merge 1 commit into
brenelz wants to merge 1 commit into
Conversation
A query() without "use server" that imports a server-only utility pulls the whole import chain into the client bundle. Server-only code then throws during client module evaluation, killing hydration before the router attaches its handlers - forms silently fall back to a native POST to the placeholder action URL and navigate to a dead page, with no diagnostic pointing at the actual mistake. Add a resolve-time guard that errors in the client environment when a module imports @solidjs/start/http, /middleware, or /config, naming the importing file and the fix. Legitimate "use server" usage is unaffected because the directives compiler strips those imports from client modules before resolution. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 1e3dc30 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
✅ Deploy Preview for solid-start-landing-page ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
commit: |
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.
Fixes #2068
What is the current behavior?
A
query()without"use server"that imports a server-only utility (the issue'screateSomeClient()using vinxi's HTTP APIs —@solidjs/start/httpon v2) pulls the entire import chain into the client bundle. Any top-level server-only code then throws during client module evaluation, which kills hydration before the router attaches its handlers — so an unrelatedaction()form on the same page silently falls back to a native POST to the SSR'd placeholderaction="https://action/…"URL and navigates to a dead page. There is no diagnostic anywhere pointing at the actual mistake.Reproduced on 2.0.0-beta.7 with a mirror of the reporter's repro: hydration dies with
process is not defined, playwright captures the nativePOST https://action/performRandomAction→chrome-error://chromewebdata/. Adding"use server"to the unused query fixes everything (causation control). The reporter's exactprocess.envvariant is masked in prod builds by Vite's define, but any real server-only import (e.g.node:async_hooks) reproduces in prod too.What is the new behavior?
A resolve-time guard (
solid-start:server-only-guard) errors in the client environment when a module imports@solidjs/start/http,@solidjs/start/middleware, or@solidjs/start/config, naming the importing file and the fix:https://action/…."use server"functions importing these helpers still work — the directives compiler strips those imports from client modules before resolution ever happens (verified: SSR renders the server-read header, client transform contains no trace of the util). API routes and middleware resolve in the SSR environment and are untouched.Other information
resolveId(id, importer, { ssr })pattern as feat: supportserver-onlyandclient-onlymodules (#2162) #2167's opt-inserver-only/client-onlymarkers — this PR effectively makes Start's own server-only entry points implicitlyserver-only. Happy to rebase onto feat: supportserver-onlyandclient-onlymodules (#2162) #2167 if it lands first.@solidjs/start/serveris deliberately not guarded: route files legitimately import types from it (e.g.APIHandler), and type-only imports aren't always distinguishable at resolve time from the raw specifier.server-only-guard.spec.ts; full playwright e2e suite inapps/testspasses (27 passed, 1 skipped).🤖 Generated with Claude Code