Skip to content

Virtual module import.meta.glob('./...') silently returns {} instead of erroring #22345

Description

@shulaoda

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:

  1. 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
  2. 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)

  1. In transformGlobImport, virtual modules pass id: undefined and dir: undefined into parseImportGlob.
  2. parseImportGlob calls toAbsoluteGlob(glob, root, importer=undefined, resolveId, base=undefined).
  3. 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.
  4. The glob(...) call therefore returns [] for the typical layout.
  5. 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

Metadata

Metadata

Assignees

Labels

inconsistencyInconsistency between dev & buildp3-minor-bugAn edge case that only affects very specific usage (priority)

Type

Fields

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions