Repository navigation
Conversation
…ite's root The server function filter's default patterns are relative, and createFilter resolved them against process.cwd(); the lazy plugin also named each module relative to process.cwd(). On Windows a working directory entered through a junction or symlink keeps that path, while Vite (8.2 and later) resolves its root and every module id to the real path. The filter then matched no module, so no "use server" function was compiled: its body shipped in the client bundle, and a build whose server functions import server-only code such as @solidjs/start/http failed with "Attempt to import 'server-only' in a client module". The lazy ids matched no client manifest key, so the CSS of lazily loaded route components was missing from the server-rendered HTML. Create the filter once the config is resolved, with config.root as the base for relative patterns, and derive lazy ids from the environment's root the way Vite derives its manifest keys. The dev style lookup resolves those ids against the root as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ Deploy Preview for solid-start-landing-page ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
🦋 Changeset detectedLatest commit: 6278519 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 |
commit: |
lxsmnsyc
approved these changes
Oct 19, 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.
PR Checklist
Please check if your PR fulfills the following requirements:
What is the current behavior?
serverFunctionsPlugincreates its filter when the plugin is created, with relative default patterns and noresolveoption, so they are resolved againstprocess.cwd(). Thelazyplugin names each module relative toprocess.cwd()as well. On Windows a working directory entered through a junction or symlink keeps that path, while Vite resolves every module id, and since 8.2 alsoconfig.root, to the real path. Then:"use server"function is compiled: its body ends up in the client bundle, and a build whose server functions import@solidjs/start/httpfails withAttempt to import 'server-only' in a client module;id$$of a lazily loaded route component matches no client manifest key, so its CSS is missing from the server-rendered HTML.#2356 has the steps.
What is the new behavior?
The filter is created in
configResolved, withconfig.rootas the base for relative patterns.lazyderivesid$$fromthis.environment.config.rootthe way Vite derives its manifest keys (normalizePath(path.relative(root, id))), and the dev style lookup resolves that id against the root instead ofprocess.cwd(). Since Vite 8.2 resolves the root like every module id (to the real path; withresolve.preserveSymlinks, since 8.2.2 the root keeps the link path as the ids do), this works when the working directory is a junction or symlink to the project; when it is the root itself, nothing changes. Relative patterns passed through theserverFunctions.filteroption now resolve against the root too.The tests make
process.cwd()differ from Vite's root, which is what a junction gives on Windows, so they run on any OS:src/directives/index.spec.ts(new): a module under the root'ssrcis compiled, one outside it is left alone, and a relative customincluderesolves against the root.src/config/lazy.spec.ts:id$$is relative to the root (without the change it is../../real/app/src/routes/page.tsx); the existing cases pass the root through the plugin context.src/server/collect-styles.spec.ts(new): the dev style lookup finds the styles of a root-relative id.Without the change, four of the five new tests fail (the guard for a module outside
srcpasses either way), and the 266 existing tests pass. A changeset is included.Other information
Checked on Windows (Node 26.10.0, Vite 8.3.4) with the basic template and the route from #2356, plus a route whose server function imports nothing. With 2.0.6, the build through a junction fails, whether the junction is on the project's drive or on another; without the server-only import it builds, but that function's body is in the client bundle. With this branch packed and installed, both junction builds pass, the client chunks hold server references instead of function bodies, and the server-rendered index page links the same stylesheets as a build from the real path.
Not covered here:
solidStart()also takesprocess.cwd()as its root (app entry,~alias, file routers), and relativecss.filterpatterns are resolved against it as well. Through a junction, the~alias treats every app module as another package's, so invite deva page that imports~/...does not finish rendering (the template's index page: the request was still open after 20 s; with a relative import it renders). The file routers match the real paths in Vite's watcher events against a glob built on the link path, so route changes are probably missed in dev (from reading the code, not tried). The template still builds only because Rolldown applies its tsconfigpathsto~; without that mapping the build fails as well.solidStart()runs before Vite resolves its root, so moving these to the root means restructuring it; I left that for a separate change. With Vite before 8.2 the root keeps the link path while module ids do not, so the filter part of this change does not help there (this repository's lockfile still resolves Vite 8.1.5); thelazyids follow Vite's own key formula either way.