From e4ef7e70601e38586be28e1847bcf59abf5e4435 Mon Sep 17 00:00:00 2001 From: Kyle Herock Date: Wed, 20 Apr 2022 16:22:47 -0400 Subject: [PATCH 1/5] test(assets): add fixture for `new URL("/...", import.meta.url)` This tests existing behavior for obtaining an asset's URL from the public base path. --- playground/assets/__tests__/assets.spec.ts | 6 ++++++ playground/assets/index.html | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index ceb259102d4031..edf4c692ec6538 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -281,6 +281,12 @@ test('new URL(..., import.meta.url)', async () => { expect(await page.textContent('.import-meta-url')).toMatch(assetMatch) }) +test('new URL("/...", import.meta.url)', async () => { + expect(await page.textContent('.import-meta-url-base-path')).toMatch( + iconMatch + ) +}) + test('new URL(`${dynamic}`, import.meta.url)', async () => { expect(await page.textContent('.dynamic-import-meta-url-1')).toMatch( isBuild ? 'data:image/png;base64' : '/foo/nested/icon.png' diff --git a/playground/assets/index.html b/playground/assets/index.html index 42fa5498f28b8c..d1df081f708326 100644 --- a/playground/assets/index.html +++ b/playground/assets/index.html @@ -182,6 +182,10 @@

new URL('...', import.meta.url)

+

new URL('/...', import.meta.url)

+ + +

new URL('...', import.meta.url,) (with comma)

@@ -354,6 +358,12 @@

style in svg

text('.import-meta-url', metaUrl) document.querySelector('.import-meta-url-img').src = metaUrl + // testing URLs for public assets served at the public base path + // equivalent to `new URL(`${import.meta.env.BASE_URL}/icon.png`, self.location) + const metaUrlBasePath = new URL('/icon.png', import.meta.url) + text('.import-meta-url-base-path', metaUrlBasePath) + document.querySelector('.import-meta-url-base-path-img').src = metaUrlBasePath + // prettier-ignore const metaUrlWithComma = new URL('./nested/asset.png', import.meta.url,) text('.import-meta-url-comma', metaUrlWithComma) From 6e1b50de6a1161a29328a759db12055564e0b09a Mon Sep 17 00:00:00 2001 From: Kyle Herock Date: Sat, 23 Apr 2022 15:43:51 -0400 Subject: [PATCH 2/5] test(worker): add fixture for `new URL(..., import.meta.url)` This test fails because the worker plugin's renderChunk hook runs its import.meta.url transformation after Rollup already has transformed it with importMeta.renderFinalMechanism. --- playground/worker/__tests__/es/es-worker.spec.ts | 5 +++++ playground/worker/__tests__/iife/iife-worker.spec.ts | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index 5dd4d4c045d834..e096a3c1f97700 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -92,6 +92,11 @@ describe.runIf(isBuild)('build', () => { }) test('module worker', async () => { + await untilUpdated( + () => page.textContent('.worker-import-meta-url'), + 'A string', + true + ) await untilUpdated( () => page.textContent('.shared-worker-import-meta-url'), 'A string', diff --git a/playground/worker/__tests__/iife/iife-worker.spec.ts b/playground/worker/__tests__/iife/iife-worker.spec.ts index b3c1ecfe28d632..b09aef8d1e94d3 100644 --- a/playground/worker/__tests__/iife/iife-worker.spec.ts +++ b/playground/worker/__tests__/iife/iife-worker.spec.ts @@ -79,6 +79,10 @@ describe.runIf(isBuild)('build', () => { }) test('module worker', async () => { + await untilUpdated( + () => page.textContent('.worker-import-meta-url'), + 'A string' + ) await untilUpdated( () => page.textContent('.shared-worker-import-meta-url'), 'A string' From 1608297d3b4300ca5b1bf2fb74158d64c00d0938 Mon Sep 17 00:00:00 2001 From: Kyle Herock Date: Wed, 20 Apr 2022 17:06:34 -0400 Subject: [PATCH 3/5] feat(assets): allow `new URL` to resolve package assets --- packages/vite/src/node/build.ts | 2 -- .../src/node/plugins/assetImportMetaUrl.ts | 33 ++++++++++++++----- packages/vite/src/node/plugins/index.ts | 2 ++ playground/assets/__tests__/assets.spec.ts | 4 +++ playground/assets/index.html | 8 +++++ 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 34d1bb98ea60fc..f3b8ee17172795 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -42,7 +42,6 @@ import { getDepsCacheDir, initDepsOptimizer } from './optimizer' -import { assetImportMetaUrlPlugin } from './plugins/assetImportMetaUrl' import { loadFallbackPlugin } from './plugins/loadFallback' import type { PackageData } from './packages' import { watchPackageDataPlugin } from './packages' @@ -310,7 +309,6 @@ export function resolveBuildPlugins(config: ResolvedConfig): { watchPackageDataPlugin(config), ...(usePluginCommonjs ? [commonjsPlugin(options.commonjsOptions)] : []), dataURIPlugin(), - assetImportMetaUrlPlugin(config), ...(options.rollupOptions.plugins ? (options.rollupOptions.plugins.filter(Boolean) as Plugin[]) : []) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index 4b52a225a68458..aad3252f3cfb71 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -1,9 +1,9 @@ -import path from 'node:path' import MagicString from 'magic-string' import { stripLiteral } from 'strip-literal' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' -import { transformStableResult } from '../utils' +import type { ResolveFn } from '../' +import { isParentDirectory, normalizePath, transformStableResult } from '../utils' import { fileToUrl } from './asset' import { preloadHelperId } from './importAnalysisBuild' @@ -18,6 +18,8 @@ import { preloadHelperId } from './importAnalysisBuild' * ``` */ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { + let assetResolver: ResolveFn + return { name: 'vite:asset-import-meta-url', async transform(code, id, options) { @@ -63,16 +65,31 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { } const url = rawUrl.slice(1, -1) - const file = path.resolve(path.dirname(id), url) - // Get final asset URL. Catch error if the file does not exist, - // in which we can resort to the initial URL and let it resolve in runtime - const builtUrl = await fileToUrl(file, config, this).catch(() => { + assetResolver ??= config.createResolver({ + root: config.publicDir, + extensions: [], + mainFields: [], + tryIndex: false, + preferRelative: true + }) + const resolved = await assetResolver(url, id) + if (!resolved) { const rawExp = code.slice(index, index + exp.length) config.logger.warnOnce( `\n${rawExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` ) - return url - }) + } + // Get final asset URL. If the file does not exist, + // we fall back to the initial URL and let it resolve in runtime + const builtUrl = resolved + ? await fileToUrl( + isParentDirectory(normalizePath(config.publicDir), resolved) + ? url + : resolved, + config, + this + ) + : url s.overwrite( index, index + exp.length, diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 75c8297bf26919..f5db400a7b7c7d 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -20,6 +20,7 @@ import { preAliasPlugin } from './preAlias' import { definePlugin } from './define' import { ssrRequireHookPlugin } from './ssrRequireHook' import { workerImportMetaUrlPlugin } from './workerImportMetaUrl' +import { assetImportMetaUrlPlugin } from './assetImportMetaUrl' import { ensureWatchPlugin } from './ensureWatch' import { metadataPlugin } from './metadata' import { dynamicImportVarsPlugin } from './dynamicImportVars' @@ -88,6 +89,7 @@ export async function resolvePlugins( isBuild && config.build.ssr ? ssrRequireHookPlugin(config) : null, isBuild && buildHtmlPlugin(config), workerImportMetaUrlPlugin(config), + assetImportMetaUrlPlugin(config), ...buildPlugins.pre, dynamicImportVarsPlugin(config), importGlobPlugin(config), diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index edf4c692ec6538..80c6c4bdcee43c 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -281,6 +281,10 @@ test('new URL(..., import.meta.url)', async () => { expect(await page.textContent('.import-meta-url')).toMatch(assetMatch) }) +test('new URL("@/...", import.meta.url)', async () => { + expect(await page.textContent('.import-meta-url-dep')).toMatch(assetMatch) +}) + test('new URL("/...", import.meta.url)', async () => { expect(await page.textContent('.import-meta-url-base-path')).toMatch( iconMatch diff --git a/playground/assets/index.html b/playground/assets/index.html index d1df081f708326..f897d61a355ed0 100644 --- a/playground/assets/index.html +++ b/playground/assets/index.html @@ -182,6 +182,10 @@

new URL('...', import.meta.url)

+

new URL('@/...', import.meta.url)

+ + +

new URL('/...', import.meta.url)

@@ -358,6 +362,10 @@

style in svg

text('.import-meta-url', metaUrl) document.querySelector('.import-meta-url-img').src = metaUrl + const metaUrlDep = new URL('@/asset.png', import.meta.url) + text('.import-meta-url-dep', metaUrlDep) + document.querySelector('.import-meta-url-dep-img').src = metaUrlDep + // testing URLs for public assets served at the public base path // equivalent to `new URL(`${import.meta.env.BASE_URL}/icon.png`, self.location) const metaUrlBasePath = new URL('/icon.png', import.meta.url) From 6f76e5a3f32e4b810cacdf95107d3b875c46f176 Mon Sep 17 00:00:00 2001 From: Kyle Herock Date: Thu, 21 Apr 2022 16:01:24 -0400 Subject: [PATCH 4/5] feat(worker): allow `new URL` to resolve workers from package aliases --- .../src/node/plugins/workerImportMetaUrl.ts | 32 +++++++++++-------- .../worker/__tests__/es/es-worker.spec.ts | 5 +++ .../worker/__tests__/iife/iife-worker.spec.ts | 4 +++ playground/worker/index.html | 6 ++++ playground/worker/vite.config-es.js | 5 +++ playground/worker/vite.config-iife.js | 5 +++ .../worker/vite.config-relative-base.js | 5 +++ playground/worker/vite.config-sourcemap.js | 5 +++ playground/worker/worker/main-module.js | 9 ++++++ 9 files changed, 63 insertions(+), 13 deletions(-) diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 5fbd7d4883382f..95c721d5953593 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -1,4 +1,3 @@ -import path from 'node:path' import JSON5 from 'json5' import MagicString from 'magic-string' import type { RollupError } from 'rollup' @@ -8,11 +7,11 @@ import type { Plugin } from '../plugin' import { cleanUrl, injectQuery, - normalizePath, parseRequest, transformStableResult } from '../utils' import { getDepsOptimizer } from '../optimizer' +import type { ResolveFn } from '..' import type { WorkerType } from './worker' import { WORKER_FILE_ID, workerFileToUrl } from './worker' import { fileToUrl } from './asset' @@ -71,6 +70,7 @@ function getWorkerType(raw: string, clean: string, i: number): WorkerType { export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const isBuild = config.command === 'build' + let workerResolver: ResolveFn return { name: 'vite:worker-import-meta-url', @@ -112,22 +112,28 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { cleanString, index + allExp.length ) - const file = normalizePath( - path.resolve(path.dirname(id), rawUrl.slice(1, -1)) - ) + const url = rawUrl.slice(1, -1) + workerResolver ??= config.createResolver({ + tryIndex: false, + preferRelative: true + }) + const file = (await workerResolver(url, id)) ?? url - let url: string + let builtUrl: string if (isBuild) { getDepsOptimizer(config, ssr)?.registerWorkersSource(id) - url = await workerFileToUrl(config, file, query) + builtUrl = await workerFileToUrl(config, file, query) } else { - url = await fileToUrl(cleanUrl(file), config, this) - url = injectQuery(url, WORKER_FILE_ID) - url = injectQuery(url, `type=${workerType}`) + builtUrl = await fileToUrl(cleanUrl(file), config, this) + builtUrl = injectQuery(builtUrl, WORKER_FILE_ID) + builtUrl = injectQuery(builtUrl, `type=${workerType}`) } - s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), { - contentOnly: true - }) + s.overwrite( + urlIndex, + urlIndex + exp.length, + `new URL(${JSON.stringify(builtUrl)}, self.location)`, + { contentOnly: true } + ) } if (s) { diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index e096a3c1f97700..eb8b9f650c236f 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -97,6 +97,11 @@ test('module worker', async () => { 'A string', true ) + await untilUpdated( + () => page.textContent('.worker-import-meta-url-resolve'), + 'A string', + true + ) await untilUpdated( () => page.textContent('.shared-worker-import-meta-url'), 'A string', diff --git a/playground/worker/__tests__/iife/iife-worker.spec.ts b/playground/worker/__tests__/iife/iife-worker.spec.ts index b09aef8d1e94d3..d6a4e784de8380 100644 --- a/playground/worker/__tests__/iife/iife-worker.spec.ts +++ b/playground/worker/__tests__/iife/iife-worker.spec.ts @@ -83,6 +83,10 @@ test('module worker', async () => { () => page.textContent('.worker-import-meta-url'), 'A string' ) + await untilUpdated( + () => page.textContent('.worker-import-meta-url-resolve'), + 'A string' + ) await untilUpdated( () => page.textContent('.shared-worker-import-meta-url'), 'A string' diff --git a/playground/worker/index.html b/playground/worker/index.html index 1b196e074d0678..5c9c85b4799fd5 100644 --- a/playground/worker/index.html +++ b/playground/worker/index.html @@ -44,6 +44,12 @@

format iife:

+

+ new Worker(new URL('@/url-worker', import.meta.url), { type: 'module' }) + .worker-import-meta-url-resolve +

+ +

new SharedWorker(new URL('./url-shared-worker.js', import.meta.url), { type: 'module' }) diff --git a/playground/worker/vite.config-es.js b/playground/worker/vite.config-es.js index 6d6704de0bc213..ecc2b4db846c6a 100644 --- a/playground/worker/vite.config-es.js +++ b/playground/worker/vite.config-es.js @@ -4,6 +4,11 @@ const vite = require('vite') module.exports = vite.defineConfig({ base: '/es/', enforce: 'pre', + resolve: { + alias: { + '@': __dirname + } + }, worker: { format: 'es', plugins: [vueJsx()], diff --git a/playground/worker/vite.config-iife.js b/playground/worker/vite.config-iife.js index 3a0578c329f8ea..0e1d59ba688564 100644 --- a/playground/worker/vite.config-iife.js +++ b/playground/worker/vite.config-iife.js @@ -3,6 +3,11 @@ const vite = require('vite') module.exports = vite.defineConfig({ base: '/iife/', + resolve: { + alias: { + '@': __dirname + } + }, worker: { format: 'iife', plugins: [ diff --git a/playground/worker/vite.config-relative-base.js b/playground/worker/vite.config-relative-base.js index 4c20940749eacc..16017c578a076c 100644 --- a/playground/worker/vite.config-relative-base.js +++ b/playground/worker/vite.config-relative-base.js @@ -4,6 +4,11 @@ const vite = require('vite') module.exports = vite.defineConfig({ base: './', + resolve: { + alias: { + '@': __dirname + } + }, worker: { format: 'es', plugins: [vueJsx()], diff --git a/playground/worker/vite.config-sourcemap.js b/playground/worker/vite.config-sourcemap.js index c4cf61f6dea586..07d57bf4ec96f1 100644 --- a/playground/worker/vite.config-sourcemap.js +++ b/playground/worker/vite.config-sourcemap.js @@ -10,6 +10,11 @@ module.exports = vite.defineConfig((sourcemap) => { base: `/iife-${ typeof sourcemap === 'boolean' ? 'sourcemap' : 'sourcemap-' + sourcemap }/`, + resolve: { + alias: { + '@': __dirname + } + }, worker: { format: 'iife', plugins: [vueJsx()], diff --git a/playground/worker/worker/main-module.js b/playground/worker/worker/main-module.js index a1205a4a7e46b8..1fbd0a689d73f9 100644 --- a/playground/worker/worker/main-module.js +++ b/playground/worker/worker/main-module.js @@ -65,6 +65,15 @@ w.addEventListener('message', (ev) => text('.worker-import-meta-url', JSON.stringify(ev.data)) ) +// url import worker with alias path +const wResolve = new Worker( + new URL('@/url-worker', import.meta.url), + /* @vite-ignore */ workerOptions +) +wResolve.addEventListener('message', (ev) => + text('.worker-import-meta-url-resolve', JSON.stringify(ev.data)) +) + const genWorkerName = () => 'module' const w2 = new SharedWorker( new URL('../url-shared-worker.js', import.meta.url), From a0255db60671b462f337ec34a1158f885808f337 Mon Sep 17 00:00:00 2001 From: Kyle Herock Date: Mon, 2 May 2022 22:57:10 -0400 Subject: [PATCH 5/5] perf: optimize URL constructors with relative paths --- .../src/node/plugins/assetImportMetaUrl.ts | 63 ++++++++++++------- .../src/node/plugins/workerImportMetaUrl.ts | 21 +++++-- playground/worker/worker/main-module.js | 2 +- 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index aad3252f3cfb71..de946614620866 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -1,9 +1,15 @@ +import path from 'node:path' import MagicString from 'magic-string' import { stripLiteral } from 'strip-literal' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' import type { ResolveFn } from '../' -import { isParentDirectory, normalizePath, transformStableResult } from '../utils' +import { + isParentDirectory, + normalizePath, + slash, + transformStableResult +} from '../utils' import { fileToUrl } from './asset' import { preloadHelperId } from './importAnalysisBuild' @@ -18,6 +24,7 @@ import { preloadHelperId } from './importAnalysisBuild' * ``` */ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { + const normalizedPublicDir = normalizePath(config.publicDir) let assetResolver: ResolveFn return { @@ -65,31 +72,45 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { } const url = rawUrl.slice(1, -1) - assetResolver ??= config.createResolver({ - root: config.publicDir, - extensions: [], - mainFields: [], - tryIndex: false, - preferRelative: true - }) - const resolved = await assetResolver(url, id) - if (!resolved) { + let file: string | undefined + if (url.startsWith('.')) { + file = slash(path.resolve(path.dirname(id), url)) + } else { + assetResolver ??= config.createResolver({ + extensions: [], + mainFields: [], + tryIndex: false, + preferRelative: true + }) + file = await assetResolver(url, id) + file ??= url.startsWith('/') + ? slash(path.join(config.publicDir, url)) + : slash(path.resolve(path.dirname(id), url)) + } + + // Get final asset URL. If the file does not exist, + // we fall back to the initial URL and let it resolve in runtime + let builtUrl: string | undefined + if (file) { + try { + if (isParentDirectory(normalizedPublicDir, file)) { + const publicPath = + '/' + path.posix.relative(normalizedPublicDir, file) + builtUrl = await fileToUrl(publicPath, config, this) + } else { + builtUrl = await fileToUrl(file, config, this) + } + } catch { + // do nothing, we'll log a warning after this + } + } + if (!builtUrl) { const rawExp = code.slice(index, index + exp.length) config.logger.warnOnce( `\n${rawExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime` ) + builtUrl = url } - // Get final asset URL. If the file does not exist, - // we fall back to the initial URL and let it resolve in runtime - const builtUrl = resolved - ? await fileToUrl( - isParentDirectory(normalizePath(config.publicDir), resolved) - ? url - : resolved, - config, - this - ) - : url s.overwrite( index, index + exp.length, diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index 95c721d5953593..ac1ee0fb545982 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -1,3 +1,4 @@ +import path from 'node:path' import JSON5 from 'json5' import MagicString from 'magic-string' import type { RollupError } from 'rollup' @@ -8,6 +9,7 @@ import { cleanUrl, injectQuery, parseRequest, + slash, transformStableResult } from '../utils' import { getDepsOptimizer } from '../optimizer' @@ -113,11 +115,20 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { index + allExp.length ) const url = rawUrl.slice(1, -1) - workerResolver ??= config.createResolver({ - tryIndex: false, - preferRelative: true - }) - const file = (await workerResolver(url, id)) ?? url + let file: string | undefined + if (url.startsWith('.')) { + file = path.resolve(path.dirname(id), url) + } else { + workerResolver ??= config.createResolver({ + extensions: [], + tryIndex: false, + preferRelative: true + }) + file = await workerResolver(url, id) + file ??= url.startsWith('/') + ? slash(path.join(config.publicDir, url)) + : slash(path.resolve(path.dirname(id), url)) + } let builtUrl: string if (isBuild) { diff --git a/playground/worker/worker/main-module.js b/playground/worker/worker/main-module.js index 1fbd0a689d73f9..913b431a7c759a 100644 --- a/playground/worker/worker/main-module.js +++ b/playground/worker/worker/main-module.js @@ -67,7 +67,7 @@ w.addEventListener('message', (ev) => // url import worker with alias path const wResolve = new Worker( - new URL('@/url-worker', import.meta.url), + new URL('@/url-worker.js', import.meta.url), /* @vite-ignore */ workerOptions ) wResolve.addEventListener('message', (ev) =>