Skip to content

Commit f4c1264

Browse files
authored
refactor: use function to eval worker and glob options (#10999)
1 parent 1ec0176 commit f4c1264

File tree

7 files changed

+145
-138
lines changed

7 files changed

+145
-138
lines changed

packages/vite/LICENSE.md

-31
Original file line numberDiff line numberDiff line change
@@ -1832,37 +1832,6 @@ Repository: git+https://github.com/isaacs/isexe.git
18321832
18331833
---------------------------------------
18341834

1835-
## json5
1836-
License: MIT
1837-
By: Aseem Kishore, Max Nanasy, Andrew Eisenberg, Jordan Tucker
1838-
Repository: git+https://github.com/json5/json5.git
1839-
1840-
> MIT License
1841-
>
1842-
> Copyright (c) 2012-2018 Aseem Kishore, and [others].
1843-
>
1844-
> Permission is hereby granted, free of charge, to any person obtaining a copy
1845-
> of this software and associated documentation files (the "Software"), to deal
1846-
> in the Software without restriction, including without limitation the rights
1847-
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1848-
> copies of the Software, and to permit persons to whom the Software is
1849-
> furnished to do so, subject to the following conditions:
1850-
>
1851-
> The above copyright notice and this permission notice shall be included in all
1852-
> copies or substantial portions of the Software.
1853-
>
1854-
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1855-
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1856-
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1857-
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1858-
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1859-
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1860-
> SOFTWARE.
1861-
>
1862-
> [others]: https://github.com/json5/json5/contributors
1863-
1864-
---------------------------------------
1865-
18661835
## launch-editor
18671836
License: MIT
18681837
By: Evan You

packages/vite/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@
9696
"etag": "^1.8.1",
9797
"fast-glob": "^3.2.12",
9898
"http-proxy": "^1.18.1",
99-
"json5": "^2.2.1",
10099
"launch-editor-middleware": "^2.6.0",
101100
"magic-string": "^0.26.7",
102101
"micromatch": "^4.0.5",

packages/vite/src/node/__tests__/plugins/importGlob/parse.test.ts

+10-12
Original file line numberDiff line numberDiff line change
@@ -327,58 +327,56 @@ describe('parse negatives', async () => {
327327
expect(
328328
await runError('import.meta.glob("hey", hey)')
329329
).toMatchInlineSnapshot(
330-
'[Error: Invalid glob import syntax: Expected the second argument o to be a object literal, but got "Identifier"]'
330+
'[Error: Invalid glob import syntax: Expected the second argument to be an object literal, but got "Identifier"]'
331331
)
332332
expect(await runError('import.meta.glob("hey", [])')).toMatchInlineSnapshot(
333-
'[Error: Invalid glob import syntax: Expected the second argument o to be a object literal, but got "ArrayExpression"]'
333+
'[Error: Invalid glob import syntax: Expected the second argument to be an object literal, but got "ArrayExpression"]'
334334
)
335335
})
336336

337337
it('options props', async () => {
338338
expect(
339339
await runError('import.meta.glob("hey", { hey: 1 })')
340-
).toMatchInlineSnapshot(
341-
'[Error: Invalid glob import syntax: Unknown options hey]'
342-
)
340+
).toMatchInlineSnapshot('[Error: Unknown glob option "hey"]')
343341
expect(
344342
await runError('import.meta.glob("hey", { import: hey })')
345343
).toMatchInlineSnapshot(
346-
'[Error: Invalid glob import syntax: Could only use literals]'
344+
'[Error: Vite is unable to parse the glob options as the value is not static]'
347345
)
348346
expect(
349347
await runError('import.meta.glob("hey", { eager: 123 })')
350348
).toMatchInlineSnapshot(
351-
'[Error: Invalid glob import syntax: Expected the type of option "eager" to be "boolean", but got "number"]'
349+
'[Error: Expected glob option "eager" to be of type boolean, but got number]'
352350
)
353351
})
354352

355353
it('options query', async () => {
356354
expect(
357355
await runError('import.meta.glob("./*.js", { as: "raw", query: "hi" })')
358356
).toMatchInlineSnapshot(
359-
'[Error: Invalid glob import syntax: Options "as" and "query" cannot be used together]'
357+
'[Error: Options "as" and "query" cannot be used together]'
360358
)
361359
expect(
362360
await runError('import.meta.glob("./*.js", { query: 123 })')
363361
).toMatchInlineSnapshot(
364-
'[Error: Invalid glob import syntax: Expected query to be a string, but got "number"]'
362+
'[Error: Expected glob option "query" to be of type object or string, but got number]'
365363
)
366364
expect(
367365
await runError('import.meta.glob("./*.js", { query: { foo: {} } })')
368366
).toMatchInlineSnapshot(
369-
'[Error: Invalid glob import syntax: Could only use literals]'
367+
'[Error: Expected glob option "query.foo" to be of type string, number, or boolean, but got object]'
370368
)
371369
expect(
372370
await runError('import.meta.glob("./*.js", { query: { foo: hey } })')
373371
).toMatchInlineSnapshot(
374-
'[Error: Invalid glob import syntax: Could only use literals]'
372+
'[Error: Vite is unable to parse the glob options as the value is not static]'
375373
)
376374
expect(
377375
await runError(
378376
'import.meta.glob("./*.js", { query: { foo: 123, ...a } })'
379377
)
380378
).toMatchInlineSnapshot(
381-
'[Error: Invalid glob import syntax: Could only use literals]'
379+
'[Error: Vite is unable to parse the glob options as the value is not static]'
382380
)
383381
})
384382
})

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

+85-73
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {
1414
TemplateLiteral
1515
} from 'estree'
1616
import { parseExpressionAt } from 'acorn'
17+
import type { RollupError } from 'rollup'
1718
import { findNodeAt } from 'acorn-walk'
1819
import MagicString from 'magic-string'
1920
import fg from 'fast-glob'
@@ -24,6 +25,7 @@ import type { ViteDevServer } from '../server'
2425
import type { ModuleNode } from '../server/moduleGraph'
2526
import type { ResolvedConfig } from '../config'
2627
import {
28+
evalValue,
2729
generateCodeFrame,
2830
normalizePath,
2931
slash,
@@ -95,14 +97,87 @@ const importGlobRE =
9597
/\bimport\.meta\.(glob|globEager|globEagerDefault)(?:<\w+>)?\s*\(/g
9698

9799
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+
}
103106

104107
const forceDefaultAs = ['raw', 'url']
105108

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+
106181
export async function parseImportGlob(
107182
code: string,
108183
importer: string | undefined,
@@ -205,82 +280,19 @@ export async function parseImportGlob(
205280
}
206281

207282
// arg2
208-
const options: GeneralImportGlobOptions = {}
283+
let options: GeneralImportGlobOptions = {}
209284
if (arg2) {
210285
if (arg2.type !== 'ObjectExpression')
211286
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}"`
220288
)
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}`)
252289

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]
272293
)
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'
277294
}
278295

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-
284296
const end = ast.range![1]
285297

286298
const globsResolved = await Promise.all(

0 commit comments

Comments
 (0)