-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: rework import.meta.glob
#7537
Changes from all commits
60c1b78
7f8150e
c0601e3
b2e01cc
a7d83bb
df0d8a3
75c978e
2abd81c
147a8f4
86c3b97
2d61513
bf147a0
b592c3f
bfe527b
1ace8af
a2e557b
4ebd33a
d16ea81
5a4b87a
544961b
8f84137
a3d5325
e44a2b3
76fa2ea
1c1fbe3
b194219
bf771dd
1cd6f2d
a8b7e53
9637203
9779078
3c3c93b
d2c6104
1c6c295
ff22800
5f9388b
2294300
ceffc8c
f4a364c
50b762a
3fe2d5c
8ac8193
74c1e0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -282,10 +282,10 @@ for (const path in modules) { | |
} | ||
``` | ||
|
||
Matched files are by default lazy loaded via dynamic import and will be split into separate chunks during build. If you'd rather import all the modules directly (e.g. relying on side-effects in these modules to be applied first), you can use `import.meta.globEager` instead: | ||
Matched files are by default lazy-loaded via dynamic import and will be split into separate chunks during build. If you'd rather import all the modules directly (e.g. relying on side-effects in these modules to be applied first), you can pass `{ eager: true }` as the second argument: | ||
|
||
```js | ||
const modules = import.meta.globEager('./dir/*.js') | ||
const modules = import.meta.glob('./dir/*.js', { eager: true }) | ||
``` | ||
|
||
The above will be transformed into the following: | ||
|
@@ -300,7 +300,9 @@ const modules = { | |
} | ||
``` | ||
|
||
`import.meta.glob` and `import.meta.globEager` also support importing files as strings (similar to [Importing Asset as String](https://vitejs.dev/guide/assets.html#importing-asset-as-string)) with the [Import Reflection](https://github.com/tc39/proposal-import-reflection) syntax: | ||
### Glob Import As | ||
|
||
`import.meta.glob` also supports importing files as strings (similar to [Importing Asset as String](https://vitejs.dev/guide/assets.html#importing-asset-as-string)) with the [Import Reflection](https://github.com/tc39/proposal-import-reflection) syntax: | ||
|
||
```js | ||
const modules = import.meta.glob('./dir/*.js', { as: 'raw' }) | ||
|
@@ -311,18 +313,115 @@ The above will be transformed into the following: | |
```js | ||
// code produced by vite | ||
const modules = { | ||
'./dir/foo.js': '{\n "msg": "foo"\n}\n', | ||
'./dir/bar.js': '{\n "msg": "bar"\n}\n' | ||
'./dir/foo.js': 'export default "foo"\n', | ||
'./dir/bar.js': 'export default "bar"\n' | ||
} | ||
``` | ||
|
||
`{ as: 'url' }` is also supported for loading assets as URLs. | ||
|
||
### Multiple Patterns | ||
|
||
The first argument can be an array of globs, for example | ||
|
||
```js | ||
const modules = import.meta.glob(['./dir/*.js', './another/*.js']) | ||
``` | ||
|
||
### Negative Patterns | ||
|
||
Negative glob patterns are also supported (prefixed with `!`). To ignore some files from the result, you can add exclude glob patterns to the first argument: | ||
|
||
```js | ||
const modules = import.meta.glob(['./dir/*.js', '!**/bar.js']) | ||
``` | ||
|
||
```js | ||
// code produced by vite | ||
const modules = { | ||
'./dir/foo.js': () => import('./dir/foo.js') | ||
} | ||
``` | ||
|
||
#### Named Imports | ||
|
||
It's possible to only import parts of the modules with the `import` options. | ||
|
||
```ts | ||
const modules = import.meta.glob('./dir/*.js', { import: 'setup' }) | ||
``` | ||
|
||
```ts | ||
// code produced by vite | ||
const modules = { | ||
'./dir/foo.js': () => import('./dir/foo.js').then((m) => m.setup), | ||
'./dir/bar.js': () => import('./dir/bar.js').then((m) => m.setup) | ||
} | ||
``` | ||
|
||
When combined with `eager` it's even possible to have tree-shaking enabled for those modules. | ||
|
||
```ts | ||
const modules = import.meta.glob('./dir/*.js', { import: 'setup', eager: true }) | ||
``` | ||
|
||
```ts | ||
// code produced by vite: | ||
import { setup as __glob__0_0 } from './dir/foo.js' | ||
import { setup as __glob__0_1 } from './dir/bar.js' | ||
const modules = { | ||
'./dir/foo.js': __glob__0_0, | ||
'./dir/bar.js': __glob__0_1 | ||
} | ||
``` | ||
|
||
Set `import` to `default` to import the default export. | ||
|
||
```ts | ||
const modules = import.meta.glob('./dir/*.js', { | ||
import: 'default', | ||
eager: true | ||
}) | ||
``` | ||
|
||
```ts | ||
// code produced by vite: | ||
import __glob__0_0 from './dir/foo.js' | ||
import __glob__0_1 from './dir/bar.js' | ||
const modules = { | ||
'./dir/foo.js': __glob__0_0, | ||
'./dir/bar.js': __glob__0_1 | ||
} | ||
``` | ||
|
||
#### Custom Queries | ||
|
||
You can also use the `query` option to provide custom queries to imports for other plugins to consume. | ||
|
||
```ts | ||
const modules = import.meta.glob('./dir/*.js', { | ||
query: { foo: 'bar', bar: true } | ||
}) | ||
``` | ||
Comment on lines
+401
to
+405
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a note, it would be good to avoid the name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess unless we are going to break our convention of using queries in v3.0, maybe naming it as |
||
|
||
```ts | ||
// code produced by vite: | ||
const modules = { | ||
'./dir/foo.js': () => | ||
import('./dir/foo.js?foo=bar&bar=true').then((m) => m.setup), | ||
'./dir/bar.js': () => | ||
import('./dir/bar.js?foo=bar&bar=true').then((m) => m.setup) | ||
} | ||
``` | ||
|
||
### Glob Import Caveats | ||
|
||
Note that: | ||
|
||
- This is a Vite-only feature and is not a web or ES standard. | ||
- The glob patterns are treated like import specifiers: they must be either relative (start with `./`) or absolute (start with `/`, resolved relative to project root) or an alias path (see [`resolve.alias` option](/config/#resolve-alias)). | ||
- The glob matching is done via `fast-glob` - check out its documentation for [supported glob patterns](https://github.com/mrmlnc/fast-glob#pattern-syntax). | ||
- You should also be aware that glob imports do not accept variables, you need to directly pass the string pattern. | ||
- The glob patterns cannot contain the same quote string (i.e. `'`, `"`, `` ` ``) as outer quotes, e.g. `'/Tom\'s files/**'`, use `"/Tom's files/**"` instead. | ||
- The glob matching is done via [`fast-glob`](https://github.com/mrmlnc/fast-glob) - check out its documentation for [supported glob patterns](https://github.com/mrmlnc/fast-glob#pattern-syntax). | ||
- You should also be aware that all the arguments in the `import.meta.glob` must be **passed as literals**. You can NOT use variables or expressions in them. | ||
|
||
## WebAssembly | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
// Vitest Snapshot v1 | ||
|
||
exports[`fixture > transform 1`] = ` | ||
"import * as __vite_glob_1_0 from \\"./modules/a.ts\\" | ||
import * as __vite_glob_1_1 from \\"./modules/b.ts\\" | ||
import * as __vite_glob_1_2 from \\"./modules/index.ts\\" | ||
import { name as __vite_glob_3_0 } from \\"./modules/a.ts\\" | ||
import { name as __vite_glob_3_1 } from \\"./modules/b.ts\\" | ||
import { name as __vite_glob_3_2 } from \\"./modules/index.ts\\" | ||
import { default as __vite_glob_5_0 } from \\"./modules/a.ts?raw\\" | ||
import { default as __vite_glob_5_1 } from \\"./modules/b.ts?raw\\" | ||
import \\"../../../../../../types/importMeta\\"; | ||
export const basic = { | ||
\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"), | ||
\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"), | ||
\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\") | ||
}; | ||
export const basicEager = { | ||
\\"./modules/a.ts\\": __vite_glob_1_0, | ||
\\"./modules/b.ts\\": __vite_glob_1_1, | ||
\\"./modules/index.ts\\": __vite_glob_1_2 | ||
}; | ||
export const ignore = { | ||
\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"), | ||
\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\") | ||
}; | ||
export const namedEager = { | ||
\\"./modules/a.ts\\": __vite_glob_3_0, | ||
\\"./modules/b.ts\\": __vite_glob_3_1, | ||
\\"./modules/index.ts\\": __vite_glob_3_2 | ||
}; | ||
export const namedDefault = { | ||
\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\").then(m => m[\\"default\\"]), | ||
\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\").then(m => m[\\"default\\"]), | ||
\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\").then(m => m[\\"default\\"]) | ||
}; | ||
export const eagerAs = { | ||
\\"./modules/a.ts\\": __vite_glob_5_0, | ||
\\"./modules/b.ts\\": __vite_glob_5_1 | ||
}; | ||
export const excludeSelf = { | ||
\\"./sibling.ts\\": () => import(\\"./sibling.ts\\") | ||
}; | ||
export const customQueryString = { | ||
\\"./sibling.ts\\": () => import(\\"./sibling.ts?custom\\") | ||
}; | ||
export const customQueryObject = { | ||
\\"./sibling.ts\\": () => import(\\"./sibling.ts?foo=bar&raw=true\\") | ||
}; | ||
export const parent = { | ||
|
||
}; | ||
export const rootMixedRelative = { | ||
\\"/css.spec.ts\\": () => import(\\"../../css.spec.ts?url\\").then(m => m[\\"default\\"]), | ||
\\"/define.spec.ts\\": () => import(\\"../../define.spec.ts?url\\").then(m => m[\\"default\\"]), | ||
\\"/import.spec.ts\\": () => import(\\"../../import.spec.ts?url\\").then(m => m[\\"default\\"]), | ||
\\"/importGlob/fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts?url\\").then(m => m[\\"default\\"]), | ||
\\"/importGlob/fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts?url\\").then(m => m[\\"default\\"]), | ||
\\"/importGlob/fixture-b/index.ts\\": () => import(\\"../fixture-b/index.ts?url\\").then(m => m[\\"default\\"]) | ||
}; | ||
export const cleverCwd1 = { | ||
\\"./node_modules/framework/pages/hello.page.js\\": () => import(\\"./node_modules/framework/pages/hello.page.js\\") | ||
}; | ||
export const cleverCwd2 = { | ||
\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"), | ||
\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"), | ||
\\"../fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts\\"), | ||
\\"../fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts\\") | ||
}; | ||
" | ||
`; | ||
|
||
exports[`fixture > transform with restoreQueryExtension 1`] = ` | ||
"import * as __vite_glob_1_0 from \\"./modules/a.ts\\" | ||
import * as __vite_glob_1_1 from \\"./modules/b.ts\\" | ||
import * as __vite_glob_1_2 from \\"./modules/index.ts\\" | ||
import { name as __vite_glob_3_0 } from \\"./modules/a.ts\\" | ||
import { name as __vite_glob_3_1 } from \\"./modules/b.ts\\" | ||
import { name as __vite_glob_3_2 } from \\"./modules/index.ts\\" | ||
import { default as __vite_glob_5_0 } from \\"./modules/a.ts?raw\\" | ||
import { default as __vite_glob_5_1 } from \\"./modules/b.ts?raw\\" | ||
import \\"../../../../../../types/importMeta\\"; | ||
export const basic = { | ||
\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"), | ||
\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"), | ||
\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\") | ||
}; | ||
export const basicEager = { | ||
\\"./modules/a.ts\\": __vite_glob_1_0, | ||
\\"./modules/b.ts\\": __vite_glob_1_1, | ||
\\"./modules/index.ts\\": __vite_glob_1_2 | ||
}; | ||
export const ignore = { | ||
\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"), | ||
\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\") | ||
}; | ||
export const namedEager = { | ||
\\"./modules/a.ts\\": __vite_glob_3_0, | ||
\\"./modules/b.ts\\": __vite_glob_3_1, | ||
\\"./modules/index.ts\\": __vite_glob_3_2 | ||
}; | ||
export const namedDefault = { | ||
\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\").then(m => m[\\"default\\"]), | ||
\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\").then(m => m[\\"default\\"]), | ||
\\"./modules/index.ts\\": () => import(\\"./modules/index.ts\\").then(m => m[\\"default\\"]) | ||
}; | ||
export const eagerAs = { | ||
\\"./modules/a.ts\\": __vite_glob_5_0, | ||
\\"./modules/b.ts\\": __vite_glob_5_1 | ||
}; | ||
export const excludeSelf = { | ||
\\"./sibling.ts\\": () => import(\\"./sibling.ts\\") | ||
}; | ||
export const customQueryString = { | ||
\\"./sibling.ts\\": () => import(\\"./sibling.ts?custom&lang.ts\\") | ||
}; | ||
export const customQueryObject = { | ||
\\"./sibling.ts\\": () => import(\\"./sibling.ts?foo=bar&raw=true&lang.ts\\") | ||
}; | ||
export const parent = { | ||
|
||
}; | ||
export const rootMixedRelative = { | ||
\\"/css.spec.ts\\": () => import(\\"../../css.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]), | ||
\\"/define.spec.ts\\": () => import(\\"../../define.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]), | ||
\\"/import.spec.ts\\": () => import(\\"../../import.spec.ts?url&lang.ts\\").then(m => m[\\"default\\"]), | ||
\\"/importGlob/fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts?url&lang.ts\\").then(m => m[\\"default\\"]), | ||
\\"/importGlob/fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts?url&lang.ts\\").then(m => m[\\"default\\"]), | ||
\\"/importGlob/fixture-b/index.ts\\": () => import(\\"../fixture-b/index.ts?url&lang.ts\\").then(m => m[\\"default\\"]) | ||
}; | ||
export const cleverCwd1 = { | ||
\\"./node_modules/framework/pages/hello.page.js\\": () => import(\\"./node_modules/framework/pages/hello.page.js\\") | ||
}; | ||
export const cleverCwd2 = { | ||
\\"./modules/a.ts\\": () => import(\\"./modules/a.ts\\"), | ||
\\"./modules/b.ts\\": () => import(\\"./modules/b.ts\\"), | ||
\\"../fixture-b/a.ts\\": () => import(\\"../fixture-b/a.ts\\"), | ||
\\"../fixture-b/b.ts\\": () => import(\\"../fixture-b/b.ts\\") | ||
}; | ||
" | ||
`; | ||
|
||
exports[`fixture > virtual modules 1`] = ` | ||
"{ | ||
\\"/modules/a.ts\\": () => import(\\"/modules/a.ts\\"), | ||
\\"/modules/b.ts\\": () => import(\\"/modules/b.ts\\"), | ||
\\"/modules/index.ts\\": () => import(\\"/modules/index.ts\\") | ||
} | ||
{ | ||
\\"/../fixture-b/a.ts\\": () => import(\\"/../fixture-b/a.ts\\"), | ||
\\"/../fixture-b/b.ts\\": () => import(\\"/../fixture-b/b.ts\\"), | ||
\\"/../fixture-b/index.ts\\": () => import(\\"/../fixture-b/index.ts\\") | ||
}" | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
!/node_modules/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are multiple imports supported with an array? Should we add a section about this or maybe just use it in the example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it does not support arrays. Referring to the
eager: true
example, the named import will be directly used as the value instead of the "Module" object. For multiple named imports they might better use multiple statements.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. Sound good since most of the cases when using glob would require a single import, so looks good to me to wait until users request the feature. We could support both string or array if we need to go there anyway.