11import path from 'node:path'
2+ import fs from 'node:fs'
23import { execSync } from 'node:child_process'
34import type * as net from 'node:net'
45import { get as httpGet } from 'node:http'
@@ -1228,10 +1229,10 @@ export async function resolveServerOptions(
12281229
12291230 let allowDirs = server . fs . allow
12301231
1232+ const cwd = searchForPackageRoot ( root )
12311233 if ( process . versions . pnp ) {
12321234 // running a command fails if cwd doesn't exist and root may not exist
12331235 // search for package root to find a path that exists
1234- const cwd = searchForPackageRoot ( root )
12351236 try {
12361237 const enableGlobalCache =
12371238 execSync ( 'yarn config get enableGlobalCache' , { cwd } )
@@ -1251,6 +1252,28 @@ export async function resolveServerOptions(
12511252 }
12521253 }
12531254
1255+ // pnpm's global virtual store (GVS) may place package files outside workspace root.
1256+ // Read node_modules/.modules.yaml which pnpm always writes on install — this works
1257+ // unconditionally regardless of how Vite is launched (node / npx / pnpm run),
1258+ // avoiding the need for subprocess calls or user-agent sniffing.
1259+ const pnpmModulesYaml = path . join ( cwd , 'node_modules' , '.modules.yaml' )
1260+ try {
1261+ const content = fs . readFileSync ( pnpmModulesYaml , 'utf-8' )
1262+ const parsed = JSON . parse ( content )
1263+ const virtualStoreDir = parsed . virtualStoreDir
1264+ if ( virtualStoreDir ) {
1265+ if ( path . isAbsolute ( virtualStoreDir ) ) {
1266+ allowDirs . push ( virtualStoreDir )
1267+ } else if ( virtualStoreDir . startsWith ( '..' ) ) {
1268+ allowDirs . push (
1269+ path . resolve ( path . join ( cwd , 'node_modules' ) , virtualStoreDir ) ,
1270+ )
1271+ }
1272+ }
1273+ } catch {
1274+ // .modules.yaml not found or unreadable — not a pnpm project, skip
1275+ }
1276+
12541277 allowDirs = allowDirs . map ( ( i ) => resolvedAllowDir ( root , i ) )
12551278
12561279 // only push client dir when vite itself is outside-of-root
0 commit comments