feat: add base option for import.meta.glob#18510
feat: add base option for import.meta.glob#18510hakshu25 wants to merge 16 commits intovitejs:mainfrom
Conversation
packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/index.ts
Outdated
Show resolved
Hide resolved
packages/vite/src/node/__tests__/plugins/importGlob/__snapshots__/fixture.spec.ts.snap
Outdated
Show resolved
Hide resolved
docs/guide/features.md
Outdated
| ```ts | ||
| // code produced by vite: | ||
| const moduleBase = { | ||
| 'dir/foo.js': () => import('./dir/foo.js'), |
There was a problem hiding this comment.
Shouldn't the key also start with ./ similar to without base?
There was a problem hiding this comment.
You're right; it should definitely be consistent. I've made the changes.
7f0f5e7
There was a problem hiding this comment.
I expect the key to be without ./ as '**/*.js' does not have ./ at the head. I think this depends on how to describe the base option. If we describe this as a prefix string, then I think we should remove ./ from the key. If we describe this as a base path, then I think we should add ./ to the key. A prefix string is more powerful than base path because it allows non-path prefix and maybe it works well with aliases, but the base path feels more natural with the name base.
There was a problem hiding this comment.
I didn't notice this PR is indirectly supporting '**/*.js' when base is set. I was thinking more like the latter, a base path to resolve the glob from and it feels easier to visualize (for me), e.g. as if you're resolving the glob in a different file path.
I think both behaviours should be equally powerful and can do the same things. But if the behaviour is more like a prefix, then perhaps the option should be called prefix, but then someone could ask for suffix too.
I also think the behaviour could be easier implemented if treated as a base path, so instead of using the file dir or root (for virtual modules), we could use whatever passed by base instead.
There was a problem hiding this comment.
I think both behaviours should be equally powerful and can do the same things.
I think the prefix one is a bit more powerful. For the following files,
foo-bar/a.jsfoo-baz/b.js
if the base behaves as prefix, you can write import.meta.glob('**/*.js', { base: 'foo-' })['bar/a.js']. But if the base behaves as base, you cannot write that and need to write import.meta.glob('./**/*.js', { base: './' })['foo-bar/a.js'].
That said, I'm fine with going with base-like behavior.
There was a problem hiding this comment.
Thanks, I was thinking in base-like and will keep the change.
There was a problem hiding this comment.
I also think we should keep base as a path and not as a prefix.
There was a problem hiding this comment.
I have modified the examples in the documentation to treat them as base instead of prefix.
500cb14
…ob-import.spec.ts
bluwy
left a comment
There was a problem hiding this comment.
Since we're treating base as the base path to resolve globs from, I think the implementation could be tweaked a bit as some still works a little like prefixes. I've made some comments below. Let me know if there's any parts unclear or would like help on.
Thanks for cleaning up the PR!
| const dir = importer ? globSafePath(dirname(importer)) : root | ||
| if (base) { | ||
| if (base.startsWith('./')) return pre + posix.join(dir, base, glob) | ||
| if (glob[0] === '@') { | ||
| const withoutAlias = /\/.+$/.exec(glob)?.[0] | ||
| return pre + posix.join(root, base, withoutAlias || glob) | ||
| } | ||
| } | ||
| glob = base ? posix.join(base, glob) : glob |
There was a problem hiding this comment.
Since base is now where we resolve globs from, I think what we should do instead is to update the dir here. For example, base should be used as dir if it's defined, otherwise we falllback to the existing logic. Like:
let dir
if (base) {
if (base.startsWith('/')) { // root-relative
dir = posix.join(root, base)
} else { // assume relative path from the current file
dir = posix.resolve(importer, base)
}
} else {
dir = importer ? globSafePath(dirname(importer)) : root
}We shouldn't need to update or return the glob ourselves. That would be like prefixing.
This assumes that base can only be ./foo (relative to the current file) or /src/foo (relative from the root), which I think should be sufficient for an initial implementation. We might want to validate in parseGlobOption that it can only start with ./, ../. or /.
There was a problem hiding this comment.
Thanks, I've made the changes below.
f6c657d
| @@ -412,19 +419,35 @@ export async function transformGlobImport( | |||
|
|
|||
| const resolvePaths = (file: string) => { | |||
| if (!dir) { | |||
There was a problem hiding this comment.
This specific dir like my other comment may need to be updated too. In the code above it leads to:
vite/packages/vite/src/node/plugins/importMetaGlob.ts
Lines 376 to 383 in ce0eec9
It should also consider base like shown in my comment. However, we don't know about base until parseImportGlob() is called.
Maybe a refactor here could be to remove this dir entirely. parseImportGlob can return and we access from here instead:
vite/packages/vite/src/node/plugins/importMetaGlob.ts
Lines 392 to 393 in ce0eec9
e.g.
async ({ globsResolved, isRelative, options, index, start, end, dir }) => {Since we only need the dir within that callback. Free to explore other options to refactor this too though.
There was a problem hiding this comment.
It would be great to make the dir reusable. I'll try refactoring it.
There was a problem hiding this comment.
@hakshu25 just a reminder about this PR. I'd love to help get it reviewed and merged. Will you be able to take a look at this comment still?
| const resolvePaths = (file: string) => { | ||
| if (!dir) { | ||
| if (isRelative) | ||
| if (!options.base && isRelative) |
There was a problem hiding this comment.
With the updates only to dir in my comments, I believe we can then revert this part, from line 422 to 450, as I think it should automatically work then.
| export const aliasBase = import.meta.glob('@modules/*.ts', { | ||
| base: '/fixture-a/modules', | ||
| }) |
There was a problem hiding this comment.
To test this correctly, this resolveId function needs to have an alias feature supported.
const resolveId = (id) => {
if (id.startsWith('@modules/')) {
return normalizePath(path.resolve(root, 'modules', id.slice('@modules/'.length)))
}
return id
}I expect the output to be same with
import.meta.glob('@modules/*.ts')if the alias works like above.
I think we should also test:
import.meta.glob('./*.ts', {
base: '@modules',
})There was a problem hiding this comment.
I expect the output to be same with
import.meta.glob('@modules/*.ts')
You're right. I was actually suggesting a different output before (#18510 (comment)). Since base is now where we resolve the globs from, but aliases will always return a root-relative key, that might be one of the downsides if treating base this way I think. Unless we want to make base unique where, if it's specified, we'll resolve the keys relative to it?
I think we should also test:
import.meta.glob('./*.ts', { base: '@modules', })
The implementation I had in mind when I added my reviews before is that base: '@modules' is a little tricky to support, and we can only support those that starts with / or ./ or ../. Since we're trying to figure out the dir to resolve from, and resolving alias would have to involve resolveId, but I don't think it handles resolving dirs well, only files.
There was a problem hiding this comment.
Since we're trying to figure out the
dirto resolve from, and resolving alias would have to involveresolveId, but I don't think it handles resolving dirs well, only files.
We are passing glob to resolveId here.
vite/packages/vite/src/node/plugins/importMetaGlob.ts
Lines 594 to 598 in 500cb14
Maybe the dirs can be resoved by something like
resolveId(base + '*').slice(0, -'*'.length)?
There was a problem hiding this comment.
Hmm yeah I guess if globs like @alias/*.ts can already be resolved here, then base should be able to do the same here too. Though if it's harder to get it right I was thinking it could be implemented later in the future. But fine by me either ways.
Description
fixes #17453.
Already #17625 is open, but it remained unresolved, so it was implemented.