Description
When a plugin produces a virtual module whose code calls import.meta.glob with a relative pattern (./*.js, ./foo.js, ...), Vite neither resolves the pattern to the user's expected location, nor throws the existing "In virtual modules, all globs must start with '/'" error. The result is an empty object with no log, no warning, and no error, which makes the failure mode very hard to debug.
Reproduction
https://stackblitz.com/edit/vitejs-vite-aoyfxbe8?file=vite.config.ts,src%2Fmain.js,index.html&terminal=dev
Minimal repro:
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
{
name: 'repro',
resolveId(id) {
if (id === 'foo') return '\0foo'
},
load(id) {
if (id === '\0foo') {
return `export default import.meta.glob('./*.js')`
}
},
},
],
})
// src/main.js
import modules from 'foo'
console.log(modules) // {}, silently empty
<!-- index.html -->
<script type="module" src="/src/main.js"></script>
.
├── index.html
├── src/
│ ├── main.js
│ └── foo.js // present, but never matched
└── vite.config.js
Run vite dev (or vite build) and observe that modules is {} and nothing is logged about the misuse.
Expected behavior
Either:
- The
import.meta.glob('./...') call in a virtual module is rejected at transform time with the documented error In virtual modules, all globs must start with '/', so the user understands they must use '/src/*.js' instead, or
- Vite resolves the pattern against a documented base for virtual modules.
Actual behavior
import.meta.glob returns an empty object. No console output, no thrown error, no warning surfaced through the plugin context. The build succeeds with the silently broken glob.
Root cause (reading packages/vite/src/node/plugins/importMetaGlob.ts)
- In
transformGlobImport, virtual modules pass id: undefined and dir: undefined into parseImportGlob.
parseImportGlob calls toAbsoluteGlob(glob, root, importer=undefined, resolveId, base=undefined).
- In
toAbsoluteGlob, because both importer and base are undefined, dir falls back to root, so ./*.js is resolved to <root>/*.js (the project root, not the actual consumer of the virtual module). Files like <root>/src/foo.js are never walked.
- The
glob(...) call therefore returns [] for the typical layout.
- The
"In virtual modules, all globs must start with '/'" guard lives inside resolvePaths, which is only invoked per matched file. With [] matches, the guard is unreachable, so the user gets no diagnostic.
Relevant lines (current main):
transformGlobImport: packages/vite/src/node/plugins/importMetaGlob.ts:422-430
- Empty-glob short circuit / per-file
resolvePaths: :454-481
toAbsoluteGlob virtual-no-base fallback to root: :650-680
Related
Description
When a plugin produces a virtual module whose code calls
import.meta.globwith a relative pattern (./*.js,./foo.js, ...), Vite neither resolves the pattern to the user's expected location, nor throws the existing"In virtual modules, all globs must start with '/'"error. The result is an empty object with no log, no warning, and no error, which makes the failure mode very hard to debug.Reproduction
https://stackblitz.com/edit/vitejs-vite-aoyfxbe8?file=vite.config.ts,src%2Fmain.js,index.html&terminal=dev
Minimal repro:
Run
vite dev(orvite build) and observe thatmodulesis{}and nothing is logged about the misuse.Expected behavior
Either:
import.meta.glob('./...')call in a virtual module is rejected at transform time with the documented errorIn virtual modules, all globs must start with '/', so the user understands they must use'/src/*.js'instead, orActual behavior
import.meta.globreturns an empty object. No console output, no thrown error, no warning surfaced through the plugin context. The build succeeds with the silently broken glob.Root cause (reading
packages/vite/src/node/plugins/importMetaGlob.ts)transformGlobImport, virtual modules passid: undefinedanddir: undefinedintoparseImportGlob.parseImportGlobcallstoAbsoluteGlob(glob, root, importer=undefined, resolveId, base=undefined).toAbsoluteGlob, because bothimporterandbaseareundefined,dirfalls back toroot, so./*.jsis resolved to<root>/*.js(the project root, not the actual consumer of the virtual module). Files like<root>/src/foo.jsare never walked.glob(...)call therefore returns[]for the typical layout."In virtual modules, all globs must start with '/'"guard lives insideresolvePaths, which is only invoked per matched file. With[]matches, the guard is unreachable, so the user gets no diagnostic.Relevant lines (current
main):transformGlobImport:packages/vite/src/node/plugins/importMetaGlob.ts:422-430resolvePaths::454-481toAbsoluteGlobvirtual-no-base fallback toroot::650-680Related
In virtual modules, all globs must start with '/'rolldown/rolldown#8729 (same scenario, panicked previously; parallel fix tracked on the rolldown plugin port)