Skip to content

Commit bafccf5

Browse files
authored
feat(assets): allow new URL to resolve package assets (#7837)
1 parent a32777f commit bafccf5

14 files changed

+157
-21
lines changed

packages/vite/src/node/build.ts

-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import {
4242
getDepsCacheDir,
4343
initDepsOptimizer
4444
} from './optimizer'
45-
import { assetImportMetaUrlPlugin } from './plugins/assetImportMetaUrl'
4645
import { loadFallbackPlugin } from './plugins/loadFallback'
4746
import type { PackageData } from './packages'
4847
import { watchPackageDataPlugin } from './packages'
@@ -310,7 +309,6 @@ export function resolveBuildPlugins(config: ResolvedConfig): {
310309
watchPackageDataPlugin(config),
311310
...(usePluginCommonjs ? [commonjsPlugin(options.commonjsOptions)] : []),
312311
dataURIPlugin(),
313-
assetImportMetaUrlPlugin(config),
314312
...(options.rollupOptions.plugins
315313
? (options.rollupOptions.plugins.filter(Boolean) as Plugin[])
316314
: [])

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

+45-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import MagicString from 'magic-string'
33
import { stripLiteral } from 'strip-literal'
44
import type { Plugin } from '../plugin'
55
import type { ResolvedConfig } from '../config'
6-
import { transformStableResult } from '../utils'
6+
import type { ResolveFn } from '../'
7+
import {
8+
isParentDirectory,
9+
normalizePath,
10+
slash,
11+
transformStableResult
12+
} from '../utils'
713
import { fileToUrl } from './asset'
814
import { preloadHelperId } from './importAnalysisBuild'
915

@@ -18,6 +24,9 @@ import { preloadHelperId } from './importAnalysisBuild'
1824
* ```
1925
*/
2026
export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
27+
const normalizedPublicDir = normalizePath(config.publicDir)
28+
let assetResolver: ResolveFn
29+
2130
return {
2231
name: 'vite:asset-import-meta-url',
2332
async transform(code, id, options) {
@@ -63,16 +72,45 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
6372
}
6473

6574
const url = rawUrl.slice(1, -1)
66-
const file = path.resolve(path.dirname(id), url)
67-
// Get final asset URL. Catch error if the file does not exist,
68-
// in which we can resort to the initial URL and let it resolve in runtime
69-
const builtUrl = await fileToUrl(file, config, this).catch(() => {
75+
let file: string | undefined
76+
if (url.startsWith('.')) {
77+
file = slash(path.resolve(path.dirname(id), url))
78+
} else {
79+
assetResolver ??= config.createResolver({
80+
extensions: [],
81+
mainFields: [],
82+
tryIndex: false,
83+
preferRelative: true
84+
})
85+
file = await assetResolver(url, id)
86+
file ??= url.startsWith('/')
87+
? slash(path.join(config.publicDir, url))
88+
: slash(path.resolve(path.dirname(id), url))
89+
}
90+
91+
// Get final asset URL. If the file does not exist,
92+
// we fall back to the initial URL and let it resolve in runtime
93+
let builtUrl: string | undefined
94+
if (file) {
95+
try {
96+
if (isParentDirectory(normalizedPublicDir, file)) {
97+
const publicPath =
98+
'/' + path.posix.relative(normalizedPublicDir, file)
99+
builtUrl = await fileToUrl(publicPath, config, this)
100+
} else {
101+
builtUrl = await fileToUrl(file, config, this)
102+
}
103+
} catch {
104+
// do nothing, we'll log a warning after this
105+
}
106+
}
107+
if (!builtUrl) {
70108
const rawExp = code.slice(index, index + exp.length)
71109
config.logger.warnOnce(
72110
`\n${rawExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime`
73111
)
74-
return url
75-
})
112+
builtUrl = url
113+
}
76114
s.overwrite(
77115
index,
78116
index + exp.length,

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

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { preAliasPlugin } from './preAlias'
2020
import { definePlugin } from './define'
2121
import { ssrRequireHookPlugin } from './ssrRequireHook'
2222
import { workerImportMetaUrlPlugin } from './workerImportMetaUrl'
23+
import { assetImportMetaUrlPlugin } from './assetImportMetaUrl'
2324
import { ensureWatchPlugin } from './ensureWatch'
2425
import { metadataPlugin } from './metadata'
2526
import { dynamicImportVarsPlugin } from './dynamicImportVars'
@@ -88,6 +89,7 @@ export async function resolvePlugins(
8889
isBuild && config.build.ssr ? ssrRequireHookPlugin(config) : null,
8990
isBuild && buildHtmlPlugin(config),
9091
workerImportMetaUrlPlugin(config),
92+
assetImportMetaUrlPlugin(config),
9193
...buildPlugins.pre,
9294
dynamicImportVarsPlugin(config),
9395
importGlobPlugin(config),

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

+29-12
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import type { Plugin } from '../plugin'
88
import {
99
cleanUrl,
1010
injectQuery,
11-
normalizePath,
1211
parseRequest,
12+
slash,
1313
transformStableResult
1414
} from '../utils'
1515
import { getDepsOptimizer } from '../optimizer'
16+
import type { ResolveFn } from '..'
1617
import type { WorkerType } from './worker'
1718
import { WORKER_FILE_ID, workerFileToUrl } from './worker'
1819
import { fileToUrl } from './asset'
@@ -75,6 +76,7 @@ function getWorkerType(raw: string, clean: string, i: number): WorkerType {
7576

7677
export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
7778
const isBuild = config.command === 'build'
79+
let workerResolver: ResolveFn
7880

7981
return {
8082
name: 'vite:worker-import-meta-url',
@@ -116,22 +118,37 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
116118
cleanString,
117119
index + allExp.length
118120
)
119-
const file = normalizePath(
120-
path.resolve(path.dirname(id), rawUrl.slice(1, -1))
121-
)
121+
const url = rawUrl.slice(1, -1)
122+
let file: string | undefined
123+
if (url.startsWith('.')) {
124+
file = path.resolve(path.dirname(id), url)
125+
} else {
126+
workerResolver ??= config.createResolver({
127+
extensions: [],
128+
tryIndex: false,
129+
preferRelative: true
130+
})
131+
file = await workerResolver(url, id)
132+
file ??= url.startsWith('/')
133+
? slash(path.join(config.publicDir, url))
134+
: slash(path.resolve(path.dirname(id), url))
135+
}
122136

123-
let url: string
137+
let builtUrl: string
124138
if (isBuild) {
125139
getDepsOptimizer(config, ssr)?.registerWorkersSource(id)
126-
url = await workerFileToUrl(config, file, query)
140+
builtUrl = await workerFileToUrl(config, file, query)
127141
} else {
128-
url = await fileToUrl(cleanUrl(file), config, this)
129-
url = injectQuery(url, WORKER_FILE_ID)
130-
url = injectQuery(url, `type=${workerType}`)
142+
builtUrl = await fileToUrl(cleanUrl(file), config, this)
143+
builtUrl = injectQuery(builtUrl, WORKER_FILE_ID)
144+
builtUrl = injectQuery(builtUrl, `type=${workerType}`)
131145
}
132-
s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), {
133-
contentOnly: true
134-
})
146+
s.overwrite(
147+
urlIndex,
148+
urlIndex + exp.length,
149+
`new URL(${JSON.stringify(builtUrl)}, self.location)`,
150+
{ contentOnly: true }
151+
)
135152
}
136153

137154
if (s) {

playground/assets/__tests__/assets.spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,16 @@ test('new URL(..., import.meta.url)', async () => {
281281
expect(await page.textContent('.import-meta-url')).toMatch(assetMatch)
282282
})
283283

284+
test('new URL("@/...", import.meta.url)', async () => {
285+
expect(await page.textContent('.import-meta-url-dep')).toMatch(assetMatch)
286+
})
287+
288+
test('new URL("/...", import.meta.url)', async () => {
289+
expect(await page.textContent('.import-meta-url-base-path')).toMatch(
290+
iconMatch
291+
)
292+
})
293+
284294
test('new URL(`${dynamic}`, import.meta.url)', async () => {
285295
expect(await page.textContent('.dynamic-import-meta-url-1')).toMatch(
286296
isBuild ? 'data:image/png;base64' : '/foo/nested/icon.png'

playground/assets/index.html

+18
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ <h2>new URL('...', import.meta.url)</h2>
182182
<img class="import-meta-url-img" />
183183
<code class="import-meta-url"></code>
184184

185+
<h2>new URL('@/...', import.meta.url)</h2>
186+
<img class="import-meta-url-dep-img" />
187+
<code class="import-meta-url-dep"></code>
188+
189+
<h2>new URL('/...', import.meta.url)</h2>
190+
<img class="import-meta-url-base-path-img" />
191+
<code class="import-meta-url-base-path"></code>
192+
185193
<h2>new URL('...', import.meta.url,) (with comma)</h2>
186194
<img class="import-meta-url-img-comma" />
187195
<code class="import-meta-url-comma"></code>
@@ -354,6 +362,16 @@ <h3>style in svg</h3>
354362
text('.import-meta-url', metaUrl)
355363
document.querySelector('.import-meta-url-img').src = metaUrl
356364

365+
const metaUrlDep = new URL('@/asset.png', import.meta.url)
366+
text('.import-meta-url-dep', metaUrlDep)
367+
document.querySelector('.import-meta-url-dep-img').src = metaUrlDep
368+
369+
// testing URLs for public assets served at the public base path
370+
// equivalent to `new URL(`${import.meta.env.BASE_URL}/icon.png`, self.location)
371+
const metaUrlBasePath = new URL('/icon.png', import.meta.url)
372+
text('.import-meta-url-base-path', metaUrlBasePath)
373+
document.querySelector('.import-meta-url-base-path-img').src = metaUrlBasePath
374+
357375
// prettier-ignore
358376
const metaUrlWithComma = new URL('./nested/asset.png', import.meta.url,)
359377
text('.import-meta-url-comma', metaUrlWithComma)

playground/worker/__tests__/es/es-worker.spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ describe.runIf(isBuild)('build', () => {
9292
})
9393

9494
test('module worker', async () => {
95+
await untilUpdated(
96+
() => page.textContent('.worker-import-meta-url'),
97+
'A string',
98+
true
99+
)
100+
await untilUpdated(
101+
() => page.textContent('.worker-import-meta-url-resolve'),
102+
'A string',
103+
true
104+
)
95105
await untilUpdated(
96106
() => page.textContent('.shared-worker-import-meta-url'),
97107
'A string',

playground/worker/__tests__/iife/iife-worker.spec.ts

+8
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ describe.runIf(isBuild)('build', () => {
7979
})
8080

8181
test('module worker', async () => {
82+
await untilUpdated(
83+
() => page.textContent('.worker-import-meta-url'),
84+
'A string'
85+
)
86+
await untilUpdated(
87+
() => page.textContent('.worker-import-meta-url-resolve'),
88+
'A string'
89+
)
8290
await untilUpdated(
8391
() => page.textContent('.shared-worker-import-meta-url'),
8492
'A string'

playground/worker/index.html

+6
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ <h2 class="format-iife">format iife:</h2>
4444
</p>
4545
<code class="worker-import-meta-url"></code>
4646

47+
<p>
48+
new Worker(new URL('@/url-worker', import.meta.url), { type: 'module' })
49+
<span class="classname">.worker-import-meta-url-resolve</span>
50+
</p>
51+
<code class="worker-import-meta-url-resolve"></code>
52+
4753
<p>
4854
new SharedWorker(new URL('./url-shared-worker.js', import.meta.url), { type:
4955
'module' })

playground/worker/vite.config-es.js

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ const vite = require('vite')
44
module.exports = vite.defineConfig({
55
base: '/es/',
66
enforce: 'pre',
7+
resolve: {
8+
alias: {
9+
'@': __dirname
10+
}
11+
},
712
worker: {
813
format: 'es',
914
plugins: [vueJsx()],

playground/worker/vite.config-iife.js

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ const vite = require('vite')
33

44
module.exports = vite.defineConfig({
55
base: '/iife/',
6+
resolve: {
7+
alias: {
8+
'@': __dirname
9+
}
10+
},
611
worker: {
712
format: 'iife',
813
plugins: [

playground/worker/vite.config-relative-base.js

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ const vite = require('vite')
44

55
module.exports = vite.defineConfig({
66
base: './',
7+
resolve: {
8+
alias: {
9+
'@': __dirname
10+
}
11+
},
712
worker: {
813
format: 'es',
914
plugins: [vueJsx()],

playground/worker/vite.config-sourcemap.js

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ module.exports = vite.defineConfig((sourcemap) => {
1010
base: `/iife-${
1111
typeof sourcemap === 'boolean' ? 'sourcemap' : 'sourcemap-' + sourcemap
1212
}/`,
13+
resolve: {
14+
alias: {
15+
'@': __dirname
16+
}
17+
},
1318
worker: {
1419
format: 'iife',
1520
plugins: [vueJsx()],

playground/worker/worker/main-module.js

+9
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ w.addEventListener('message', (ev) =>
6565
text('.worker-import-meta-url', JSON.stringify(ev.data))
6666
)
6767

68+
// url import worker with alias path
69+
const wResolve = new Worker(
70+
new URL('@/url-worker.js', import.meta.url),
71+
/* @vite-ignore */ workerOptions
72+
)
73+
wResolve.addEventListener('message', (ev) =>
74+
text('.worker-import-meta-url-resolve', JSON.stringify(ev.data))
75+
)
76+
6877
const genWorkerName = () => 'module'
6978
const w2 = new SharedWorker(
7079
new URL('../url-shared-worker.js', import.meta.url),

0 commit comments

Comments
 (0)