Turbopack: Add import.meta.glob support (Vite compat) (#92640)
## What?
Adds support for [Vite's
`import.meta.glob`](https://vite.dev/guide/features.html#glob-import) in
Turbopack. This is a compile-time transform that resolves glob patterns
into a map of module paths to lazy/eager imports.
## Why?
This is a commonly used Vite feature for dynamically importing groups of
files (e.g., all markdown files in a directory, all route modules
matching a pattern).
## How?
### Core implementation (`import_meta_glob.rs`)
1. **Analysis phase**: `import.meta.glob` is recognized as a well-known
function via `WellKnownFunctionKind::ImportMetaGlob`. When called,
arguments are statically analyzed to extract patterns and options.
2. **File discovery**: Uses Turbopack's `read_glob` (with
`Glob::can_match_in_directory` for efficient directory pruning) to find
matching files. Negative patterns (prefixed with `!`) are applied via a
separate `Glob` matcher.
3. **Virtual module**: Each unique `import.meta.glob()` call generates a
virtual `ImportMetaGlobAsset` module that exports an object mapping file
paths to either:
- **Lazy mode** (default): `() => import('./path')` thunks — resolved
with `EcmaScriptModulesReferenceSubType::DynamicImport`
- **Eager mode**: Direct `require('./path')` results — resolved with
`EcmaScriptModulesReferenceSubType::Import`
4. **Code generation**: The `import.meta.glob(...)` call site is
replaced with `__turbopack_require__(virtual_module_id)`.
### Architecture
- **`ImportMetaGlobAsset`** is the virtual module. It stores only the
origin, patterns, and options — no `source` reference.
- **`ImportMetaGlobAsset::map()`** is a `#[turbo_tasks::function]` that
builds glob matchers, scans the filesystem, and resolves all matched
files as ESM imports. Being a turbo-tasks function, the result is
memoised. Both `references()` and `chunk_item_content()` call this
single cached function.
- **`ident()`** is derived from `AssetIdent::from_path(origin_path)`
with a modifier encoding all glob options, so two `import.meta.glob()`
calls with different options produce different module idents.
- **Side effects**: Lazy mode → `SideEffectFree` (only exports thunks,
nothing evaluated). Eager mode → `ModuleEvaluationIsSideEffectFree` (the
virtual module itself has no side effects, but its synchronous requires
trigger real module evaluations).
### Supported options
| Option | Description |
|--------|-------------|
| `eager` | `boolean` — load modules synchronously (default: `false`) |
| `import` | `string` — select a named export (e.g., `'default'`) |
| `query` | `string` — append query to imports (e.g., `'?raw'`) |
| `base` | `string` — base directory for glob scanning |
### Error handling
Unsupported or invalid usage produces clear compile-time diagnostics
(error code `TP1008`):
- `as` option → "not supported, use `query` instead"
- Unknown option keys → lists supported options
- Non-constant `eager` → "must be a constant boolean"
- Non-constant patterns → "must be string literals"
### Not supported (intentionally)
- `import.meta.globEager()` (removed in Vite 3) — users should use `{
eager: true }`
- `as` option (deprecated in Vite 5) — users should use `query`
## Test Plan
- [x] **Turbopack execution test**
(`turbopack/crates/turbopack-tests/tests/execution/turbopack/resolving/import-meta-glob/`)
- Lazy mode, eager mode, named import (`import: 'default'`), negative
patterns
- [x] **Turbopack snapshot tests**
(`turbopack/crates/turbopack-tests/tests/snapshot/import-meta/glob/`)
- Lazy glob, eager glob, named import
- Negative patterns (`!**/bar.js`)
- Multiple patterns (`['./dir/*.js', './other/*.js']`)
- [x] **Turbopack snapshot error tests**
(`turbopack/crates/turbopack-tests/tests/snapshot/import-meta/glob-error/`)
- `as` option error, non-constant `eager` error, unknown option error
- [x] **Next.js e2e tests** (`test/e2e/import-meta-glob/`)
- Lazy/eager/named import modules, negative patterns, multi-pattern
- Passes in both dev (`next dev`) and production (`next build && next
start`) modes
- Skipped under webpack (`IS_WEBPACK_TEST=1`) since this is a
Turbopack-only feature
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Niklas Mischkulnig <mischnic@users.noreply.github.com>