-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
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: add base option for import.meta.glob #18510
base: main
Are you sure you want to change the base?
feat: add base option for import.meta.glob #18510
Conversation
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.
Thank you for the PR!
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the key also start with ./
similar to without base?
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.
You're right; it should definitely be consistent. I've made the changes.
7f0f5e7
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.js
foo-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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I was thinking in base-like and will keep the change.
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.
I also think we should keep base as a path and not as a prefix.
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.
I have modified the examples in the documentation to treat them as base instead of prefix.
500cb14
…ob-import.spec.ts
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
const dir = isVirtual ? undefined : dirname(id) | |
const matches = await parseImportGlob( | |
code, | |
isVirtual ? undefined : id, | |
root, | |
resolveId, | |
logger, | |
) |
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
matches.map( | |
async ({ globsResolved, isRelative, options, index, start, end }) => { |
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to make the dir
reusable. I'll try refactoring it.
@@ -412,19 +419,35 @@ export async function transformGlobImport( | |||
|
|||
const resolvePaths = (file: string) => { | |||
if (!dir) { | |||
if (isRelative) | |||
if (!options.base && isRelative) |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To test this correctly, this resolveId
function needs to have an alias feature supported.
const resolveId = (id: string) => id |
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're trying to figure out the
dir
to 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
const resolved = normalizePath( | |
(await resolveId(glob, importer, { | |
custom: { 'vite:import-glob': { isSubImportsPattern } }, | |
})) || glob, | |
) |
Maybe the dirs can be resoved by something like
resolveId(base + '*').slice(0, -'*'.length)
?
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.
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.