@@ -14,6 +14,7 @@ import type {
14
14
TemplateLiteral
15
15
} from 'estree'
16
16
import { parseExpressionAt } from 'acorn'
17
+ import type { RollupError } from 'rollup'
17
18
import { findNodeAt } from 'acorn-walk'
18
19
import MagicString from 'magic-string'
19
20
import fg from 'fast-glob'
@@ -24,6 +25,7 @@ import type { ViteDevServer } from '../server'
24
25
import type { ModuleNode } from '../server/moduleGraph'
25
26
import type { ResolvedConfig } from '../config'
26
27
import {
28
+ evalValue ,
27
29
generateCodeFrame ,
28
30
normalizePath ,
29
31
slash ,
@@ -95,14 +97,87 @@ const importGlobRE =
95
97
/ \b i m p o r t \. m e t a \. ( g l o b | g l o b E a g e r | g l o b E a g e r D e f a u l t ) (?: < \w + > ) ? \s * \( / g
96
98
97
99
const knownOptions = {
98
- as : 'string' ,
99
- eager : 'boolean' ,
100
- import : 'string' ,
101
- exhaustive : 'boolean'
102
- } as const
100
+ as : [ 'string' ] ,
101
+ eager : [ 'boolean' ] ,
102
+ import : [ 'string' ] ,
103
+ exhaustive : [ 'boolean' ] ,
104
+ query : [ 'object' , 'string' ]
105
+ }
103
106
104
107
const forceDefaultAs = [ 'raw' , 'url' ]
105
108
109
+ function err ( e : string , pos : number ) {
110
+ const error = new Error ( e ) as RollupError
111
+ error . pos = pos
112
+ return error
113
+ }
114
+
115
+ function parseGlobOptions (
116
+ rawOpts : string ,
117
+ optsStartIndex : number
118
+ ) : GeneralImportGlobOptions {
119
+ let opts : GeneralImportGlobOptions = { }
120
+ try {
121
+ opts = evalValue ( rawOpts )
122
+ } catch {
123
+ throw err (
124
+ 'Vite is unable to parse the glob options as the value is not static' ,
125
+ optsStartIndex
126
+ )
127
+ }
128
+
129
+ if ( opts == null ) {
130
+ return { }
131
+ }
132
+
133
+ for ( const key in opts ) {
134
+ if ( ! ( key in knownOptions ) ) {
135
+ throw err ( `Unknown glob option "${ key } "` , optsStartIndex )
136
+ }
137
+ const allowedTypes = knownOptions [ key as keyof typeof knownOptions ]
138
+ const valueType = typeof opts [ key as keyof GeneralImportGlobOptions ]
139
+ if ( ! allowedTypes . includes ( valueType ) ) {
140
+ throw err (
141
+ `Expected glob option "${ key } " to be of type ${ allowedTypes . join (
142
+ ' or '
143
+ ) } , but got ${ valueType } `,
144
+ optsStartIndex
145
+ )
146
+ }
147
+ }
148
+
149
+ if ( typeof opts . query === 'object' ) {
150
+ for ( const key in opts . query ) {
151
+ const value = opts . query [ key ]
152
+ if ( ! [ 'string' , 'number' , 'boolean' ] . includes ( typeof value ) ) {
153
+ throw err (
154
+ `Expected glob option "query.${ key } " to be of type string, number, or boolean, but got ${ typeof value } ` ,
155
+ optsStartIndex
156
+ )
157
+ }
158
+ }
159
+ }
160
+
161
+ if ( opts . as && forceDefaultAs . includes ( opts . as ) ) {
162
+ if ( opts . import && opts . import !== 'default' && opts . import !== '*' )
163
+ throw err (
164
+ `Option "import" can only be "default" or "*" when "as" is "${ opts . as } ", but got "${ opts . import } "` ,
165
+ optsStartIndex
166
+ )
167
+ opts . import = opts . import || 'default'
168
+ }
169
+
170
+ if ( opts . as && opts . query )
171
+ throw err (
172
+ 'Options "as" and "query" cannot be used together' ,
173
+ optsStartIndex
174
+ )
175
+
176
+ if ( opts . as ) opts . query = opts . as
177
+
178
+ return opts
179
+ }
180
+
106
181
export async function parseImportGlob (
107
182
code : string ,
108
183
importer : string | undefined ,
@@ -205,82 +280,19 @@ export async function parseImportGlob(
205
280
}
206
281
207
282
// arg2
208
- const options : GeneralImportGlobOptions = { }
283
+ let options : GeneralImportGlobOptions = { }
209
284
if ( arg2 ) {
210
285
if ( arg2 . type !== 'ObjectExpression' )
211
286
throw err (
212
- `Expected the second argument o to be a object literal, but got "${ arg2 . type } "`
213
- )
214
-
215
- for ( const property of arg2 . properties ) {
216
- if (
217
- property . type === 'SpreadElement' ||
218
- ( property . key . type !== 'Identifier' &&
219
- property . key . type !== 'Literal' )
287
+ `Expected the second argument to be an object literal, but got "${ arg2 . type } "`
220
288
)
221
- throw err ( 'Could only use literals' )
222
-
223
- const name = ( ( property . key as any ) . name ||
224
- ( property . key as any ) . value ) as keyof GeneralImportGlobOptions
225
- if ( name === 'query' ) {
226
- if ( property . value . type === 'ObjectExpression' ) {
227
- const data : Record < string , string > = { }
228
- for ( const prop of property . value . properties ) {
229
- if (
230
- prop . type === 'SpreadElement' ||
231
- prop . key . type !== 'Identifier' ||
232
- prop . value . type !== 'Literal'
233
- )
234
- throw err ( 'Could only use literals' )
235
- data [ prop . key . name ] = prop . value . value as any
236
- }
237
- options . query = data
238
- } else if ( property . value . type === 'Literal' ) {
239
- if ( typeof property . value . value !== 'string' )
240
- throw err (
241
- `Expected query to be a string, but got "${ typeof property . value
242
- . value } "`
243
- )
244
- options . query = property . value . value
245
- } else {
246
- throw err ( 'Could only use literals' )
247
- }
248
- continue
249
- }
250
-
251
- if ( ! ( name in knownOptions ) ) throw err ( `Unknown options ${ name } ` )
252
289
253
- if ( property . value . type !== 'Literal' )
254
- throw err ( 'Could only use literals' )
255
-
256
- const valueType = typeof property . value . value
257
- if ( valueType === 'undefined' ) continue
258
-
259
- if ( valueType !== knownOptions [ name ] )
260
- throw err (
261
- `Expected the type of option "${ name } " to be "${ knownOptions [ name ] } ", but got "${ valueType } "`
262
- )
263
- options [ name ] = property . value . value as any
264
- }
265
- }
266
-
267
- if ( options . as && forceDefaultAs . includes ( options . as ) ) {
268
- if (
269
- options . import &&
270
- options . import !== 'default' &&
271
- options . import !== '*'
290
+ options = parseGlobOptions (
291
+ code . slice ( arg2 . range ! [ 0 ] , arg2 . range ! [ 1 ] ) ,
292
+ arg2 . range ! [ 0 ]
272
293
)
273
- throw err (
274
- `Option "import" can only be "default" or "*" when "as" is "${ options . as } ", but got "${ options . import } "`
275
- )
276
- options . import = options . import || 'default'
277
294
}
278
295
279
- if ( options . as && options . query )
280
- throw err ( 'Options "as" and "query" cannot be used together' )
281
-
282
- if ( options . as ) options . query = options . as
283
-
284
296
const end = ast . range ! [ 1 ]
285
297
286
298
const globsResolved = await Promise . all (
0 commit comments