Skip to content

Commit 159cc79

Browse files
committed
feat: support absolute glob patterns
close #1875
1 parent f90a85c commit 159cc79

File tree

7 files changed

+63
-38
lines changed

7 files changed

+63
-38
lines changed

docs/guide/features.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ const modules = {
221221
Note that:
222222

223223
- This is a Vite-only feature and is not a web or ES standard.
224-
- The glob patterns must be relative and start with `.`.
224+
- The glob patterns are treated like import specifiers: they must be either relative (start with `./`) or absolute (start with `/`, resolved relative to project root). Globbing from dependencies is not supported.
225225
- The glob matching is done via `fast-glob` - check out its documentation for [supported glob patterns](https://github.com/mrmlnc/fast-glob#pattern-syntax).
226226

227227
## Web Assembly

packages/playground/glob-import/__tests__/glob-import.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ const json = isBuild
3030

3131
const allResult = {
3232
// JSON file should be properly transformed
33-
'./dir/baz.json': json,
34-
'./dir/foo.js': {
33+
'/dir/baz.json': json,
34+
'/dir/foo.js': {
3535
msg: 'foo'
3636
},
37-
'./dir/index.js': {
37+
'/dir/index.js': {
3838
modules: filteredResult
3939
},
40-
'./dir/nested/bar.js': {
40+
'/dir/nested/bar.js': {
4141
modules: {
4242
'../baz.json': json
4343
},
@@ -58,7 +58,7 @@ if (!isBuild) {
5858
() => page.textContent('.result'),
5959
JSON.stringify(
6060
{
61-
'./dir/a.js': {},
61+
'/dir/a.js': {},
6262
...allResult
6363
},
6464
null,
@@ -72,7 +72,7 @@ if (!isBuild) {
7272
() => page.textContent('.result'),
7373
JSON.stringify(
7474
{
75-
'./dir/a.js': {
75+
'/dir/a.js': {
7676
msg: 'a'
7777
},
7878
...allResult

packages/playground/glob-import/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<script type="module" src="./dir/index.js"></script>
44
<script type="module">
5-
const modules = import.meta.glob('./dir/**')
5+
const modules = import.meta.glob('/dir/**')
66

77
for (const path in modules) {
88
modules[path]().then((mod) => {

packages/vite/src/node/importGlob.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export async function transformImportGlob(
1313
pos: number,
1414
importer: string,
1515
importIndex: number,
16+
root: string,
1617
normalizeUrl?: (url: string, pos: number) => Promise<[string, string]>
1718
): Promise<{
1819
importsString: string
@@ -35,18 +36,25 @@ export async function transformImportGlob(
3536
const importerBasename = path.basename(importer)
3637

3738
let [pattern, endIndex] = lexGlobPattern(source, pos)
38-
if (!pattern.startsWith('.')) {
39-
throw err(`pattern must start with "."`)
39+
if (!pattern.startsWith('.') && !pattern.startsWith('/')) {
40+
throw err(`pattern must start with "." or "/" (relative to project root)`)
4041
}
41-
let base = path.dirname(importer)
42+
let base
4243
let parentDepth = 0
43-
while (pattern.startsWith('../')) {
44-
pattern = pattern.slice(3)
45-
base = path.resolve(base, '../')
46-
parentDepth++
47-
}
48-
if (pattern.startsWith('./')) {
49-
pattern = pattern.slice(2)
44+
let isAbsolute = pattern.startsWith('/')
45+
if (isAbsolute) {
46+
base = path.resolve(root)
47+
pattern = pattern.slice(1)
48+
} else {
49+
base = path.dirname(importer)
50+
while (pattern.startsWith('../')) {
51+
pattern = pattern.slice(3)
52+
base = path.resolve(base, '../')
53+
parentDepth++
54+
}
55+
if (pattern.startsWith('./')) {
56+
pattern = pattern.slice(2)
57+
}
5058
}
5159
const files = glob.sync(pattern, { cwd: base })
5260
const imports: string[] = []
@@ -55,7 +63,9 @@ export async function transformImportGlob(
5563
for (let i = 0; i < files.length; i++) {
5664
// skip importer itself
5765
if (files[i] === importerBasename) continue
58-
const file = parentDepth
66+
const file = isAbsolute
67+
? `/${files[i]}`
68+
: parentDepth
5969
? `${'../'.repeat(parentDepth)}${files[i]}`
6070
: `./${files[i]}`
6171
let importee = file

packages/vite/src/node/optimizer/scan.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,12 @@ function esbuildScanPlugin(
200200
}
201201

202202
if (js.includes('import.meta.glob')) {
203-
return transformGlob(js, path, loader).then((contents) => ({
204-
loader,
205-
contents
206-
}))
203+
return transformGlob(js, path, config.root, loader).then(
204+
(contents) => ({
205+
loader,
206+
contents
207+
})
208+
)
207209
}
208210

209211
return {
@@ -310,7 +312,7 @@ function esbuildScanPlugin(
310312
}
311313

312314
if (contents.includes('import.meta.glob')) {
313-
return transformGlob(contents, id, ext as Loader).then(
315+
return transformGlob(contents, id, config.root, ext as Loader).then(
314316
(contents) => ({
315317
loader: ext as Loader,
316318
contents
@@ -326,7 +328,12 @@ function esbuildScanPlugin(
326328
}
327329
}
328330

329-
async function transformGlob(source: string, importer: string, loader: Loader) {
331+
async function transformGlob(
332+
source: string,
333+
importer: string,
334+
root: string,
335+
loader: Loader
336+
) {
330337
// transform the content first since es-module-lexer can't handle non-js
331338
if (loader !== 'js') {
332339
source = (await (await ensureService()).transform(source, { loader })).code
@@ -344,7 +351,8 @@ async function transformGlob(source: string, importer: string, loader: Loader) {
344351
source,
345352
start,
346353
normalizePath(importer),
347-
index
354+
index,
355+
root
348356
)
349357
s.prepend(importsString)
350358
s.overwrite(expStart, endIndex, exp)

packages/vite/src/node/plugins/importAnalysis.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ function markExplicitImport(url: string) {
8585
* ```
8686
*/
8787
export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
88-
const clientPublicPath = path.posix.join(config.base, CLIENT_PUBLIC_PATH)
88+
const { root, base } = config
89+
const clientPublicPath = path.posix.join(base, CLIENT_PUBLIC_PATH)
90+
8991
let server: ViteDevServer
9092

9193
return {
@@ -96,7 +98,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
9698
},
9799

98100
async transform(source, importer, ssr) {
99-
const prettyImporter = prettifyUrl(importer, config.root)
101+
const prettyImporter = prettifyUrl(importer, root)
100102

101103
if (canSkip(importer)) {
102104
isDebug && debugRewrite(chalk.dim(`[skipped] ${prettyImporter}`))
@@ -162,8 +164,8 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
162164
url: string,
163165
pos: number
164166
): Promise<[string, string]> => {
165-
if (config.base !== '/' && url.startsWith(config.base)) {
166-
url = url.replace(config.base, '/')
167+
if (base !== '/' && url.startsWith(base)) {
168+
url = url.replace(base, '/')
167169
}
168170

169171
const resolved = await this.resolve(url, importer)
@@ -179,9 +181,9 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
179181

180182
// normalize all imports into resolved URLs
181183
// e.g. `import 'foo'` -> `import '/@fs/.../node_modules/foo/index.js`
182-
if (resolved.id.startsWith(config.root + '/')) {
184+
if (resolved.id.startsWith(root + '/')) {
183185
// in root: infer short absolute path from root
184-
url = resolved.id.slice(config.root.length)
186+
url = resolved.id.slice(root.length)
185187
} else if (fs.existsSync(cleanUrl(resolved.id))) {
186188
// exists but out of root: rewrite to absolute /@fs/ paths
187189
url = path.posix.join(FS_PREFIX + resolved.id)
@@ -234,7 +236,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
234236
}
235237

236238
// prepend base (dev base is guaranteed to have ending slash)
237-
url = config.base + url.replace(/^\//, '')
239+
url = base + url.replace(/^\//, '')
238240
return [url, resolved.id]
239241
}
240242

@@ -284,13 +286,12 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
284286
start,
285287
importer,
286288
index,
289+
root,
287290
normalizeUrl
288291
)
289292
str().prepend(importsString)
290293
str().overwrite(expStart, endIndex, exp)
291-
imports.forEach((url) =>
292-
importedUrls.add(url.replace(config.base, '/'))
293-
)
294+
imports.forEach((url) => importedUrls.add(url.replace(base, '/')))
294295
server._globImporters[importerModule.file!] = {
295296
module: importerModule,
296297
base,
@@ -386,7 +387,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
386387

387388
// record for HMR import chain analysis
388389
// make sure to normalize away base
389-
importedUrls.add(url.replace(config.base, '/'))
390+
importedUrls.add(url.replace(base, '/'))
390391
} else if (!importer.startsWith(clientDir) && !ssr) {
391392
if (!hasViteIgnore && !isSupportedDynamicImport(url)) {
392393
this.warn(

packages/vite/src/node/plugins/importAnaysisBuild.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,13 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
132132
exp,
133133
endIndex,
134134
isEager
135-
} = await transformImportGlob(source, start, importer, index)
135+
} = await transformImportGlob(
136+
source,
137+
start,
138+
importer,
139+
index,
140+
config.root
141+
)
136142
str().prepend(importsString)
137143
str().overwrite(expStart, endIndex, exp)
138144
if (!isEager) {

0 commit comments

Comments
 (0)