diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 6c8e0169eeeab4..fffeae697abc8b 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -3,6 +3,7 @@ import path from 'node:path' import colors from 'picocolors' import type { ExternalOption, + InternalModuleFormat, ModuleFormat, OutputOptions, Plugin, @@ -826,6 +827,50 @@ function injectSsrFlag>( return { ...(options ?? {}), ssr: true } as T & { ssr: boolean } } +/* + The following functions are copied from rollup + https://github.com/rollup/rollup/blob/c5269747cd3dd14c4b306e8cea36f248d9c1aa01/src/ast/nodes/MetaProperty.ts#L189-L232 + + https://github.com/rollup/rollup + The MIT License (MIT) + Copyright (c) 2017 [these people](https://github.com/rollup/rollup/graphs/contributors) + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +const getResolveUrl = (path: string, URL = 'URL') => `new ${URL}(${path}).href` + +const getRelativeUrlFromDocument = (relativePath: string, umd = false) => + getResolveUrl( + `'${relativePath}', ${ + umd ? `typeof document === 'undefined' ? location.href : ` : '' + }document.currentScript && document.currentScript.src || document.baseURI` + ) + +const relativeUrlMechanisms: Record< + InternalModuleFormat, + (relativePath: string) => string +> = { + amd: (relativePath) => { + if (relativePath[0] !== '.') relativePath = './' + relativePath + return getResolveUrl(`require.toUrl('${relativePath}'), document.baseURI`) + }, + cjs: (relativePath) => + `(typeof document === 'undefined' ? ${getResolveUrl( + `'file:' + __dirname + '/${relativePath}'`, + `(require('u' + 'rl').URL)` + )} : ${getRelativeUrlFromDocument(relativePath)})`, + es: (relativePath) => getResolveUrl(`'${relativePath}', import.meta.url`), + iife: (relativePath) => getRelativeUrlFromDocument(relativePath), + system: (relativePath) => getResolveUrl(`'${relativePath}', module.meta.url`), + umd: (relativePath) => + `(typeof document === 'undefined' && typeof location === 'undefined' ? ${getResolveUrl( + `'file:' + __dirname + '/${relativePath}'`, + `(require('u' + 'rl').URL)` + )} : ${getRelativeUrlFromDocument(relativePath, true)})` +} +/* end of copy */ + export type RenderBuiltAssetUrl = ( filename: string, type: { @@ -842,10 +887,13 @@ export function toOutputFilePathInString( hostId: string, hostType: 'js' | 'css' | 'html', config: ResolvedConfig, + format: InternalModuleFormat, toRelative: ( filename: string, hostType: string - ) => string | { runtime: string } = toImportMetaURLBasedRelativePath + ) => string | { runtime: string } = getToImportMetaURLBasedRelativePath( + format + ) ): string | { runtime: string } { const { renderBuiltUrl } = config.experimental let relative = config.base === '' || config.base === './' @@ -873,15 +921,15 @@ export function toOutputFilePathInString( return config.base + filename } -function toImportMetaURLBasedRelativePath( - filename: string, - importer: string -): { runtime: string } { - return { - runtime: `new URL(${JSON.stringify( +function getToImportMetaURLBasedRelativePath( + format: InternalModuleFormat +): (filename: string, importer: string) => { runtime: string } { + const toRelativePath = relativeUrlMechanisms[format] + return (filename, importer) => ({ + runtime: toRelativePath( path.posix.relative(path.dirname(importer), filename) - )},import.meta.url).href` - } + ) + }) } export function toOutputFilePathWithoutRuntime( diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 719269c5934290..0db1301a876fdb 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -90,7 +90,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { return `export default ${JSON.stringify(url)}` }, - renderChunk(code, chunk) { + renderChunk(code, chunk, outputOptions) { let match: RegExpExecArray | null let s: MagicString | undefined @@ -115,7 +115,8 @@ export function assetPlugin(config: ResolvedConfig): Plugin { 'asset', chunk.fileName, 'js', - config + config, + outputOptions.format ) const replacementString = typeof replacement === 'string' @@ -138,7 +139,8 @@ export function assetPlugin(config: ResolvedConfig): Plugin { 'public', chunk.fileName, 'js', - config + config, + outputOptions.format ) const replacementString = typeof replacement === 'string' diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index fe2f209b915b60..162a7c2f0f58da 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -308,7 +308,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } }, - renderChunk(code, chunk) { + renderChunk(code, chunk, outputOptions) { let s: MagicString const result = () => { return ( @@ -334,7 +334,8 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { 'asset', chunk.fileName, 'js', - config + config, + outputOptions.format ) const replacementString = typeof replacement === 'string' @@ -349,12 +350,6 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } ) } - - // TODO: check if this should be removed - if (config.isWorker) { - s = s.replace('import.meta.url', 'self.location.href') - return result() - } } if (!isWorker) { const workerMap = workerCache.get(config)! diff --git a/playground/legacy/__tests__/legacy.spec.ts b/playground/legacy/__tests__/legacy.spec.ts index e9375e051efa6d..ac9b3d524cf9d5 100644 --- a/playground/legacy/__tests__/legacy.spec.ts +++ b/playground/legacy/__tests__/legacy.spec.ts @@ -63,6 +63,12 @@ test('should load dynamic import with css', async () => { await untilUpdated(() => getColor('#dynamic-css'), 'red', true) }) +test('asset url', async () => { + expect(await page.textContent('#asset-path')).toMatch( + isBuild ? /\/assets\/vite\.\w+\.svg/ : '/vite.svg' + ) +}) + describe.runIf(isBuild)('build', () => { test('should generate correct manifest', async () => { const manifest = readManifest() diff --git a/playground/legacy/index.html b/playground/legacy/index.html index cbf6242fad756b..e2bf74c7d4efaa 100644 --- a/playground/legacy/index.html +++ b/playground/legacy/index.html @@ -6,4 +6,5 @@

+
diff --git a/playground/legacy/main.js b/playground/legacy/main.js index 24959b129f28f2..3a9714cadfea72 100644 --- a/playground/legacy/main.js +++ b/playground/legacy/main.js @@ -1,5 +1,5 @@ import './style.css' -import './vite.svg' +import viteSvgPath from './vite.svg' async function run() { const { fn } = await import('./async.js') @@ -51,6 +51,8 @@ document text('#dynamic-css', 'dynamic import css') }) +text('#asset-path', viteSvgPath) + function text(el, text) { document.querySelector(el).textContent = text } diff --git a/playground/worker/__tests__/es/es-worker.spec.ts b/playground/worker/__tests__/es/es-worker.spec.ts index f65d10a3dbcc05..2bffbb69df6502 100644 --- a/playground/worker/__tests__/es/es-worker.spec.ts +++ b/playground/worker/__tests__/es/es-worker.spec.ts @@ -14,6 +14,11 @@ test('normal', async () => { 'worker bundle with plugin success!', true ) + await untilUpdated( + () => page.textContent('.asset-url'), + isBuild ? '/es/assets/vite.svg' : '/es/vite.svg', + true + ) }) test('TS output', async () => { @@ -51,7 +56,7 @@ describe.runIf(isBuild)('build', () => { test('inlined code generation', async () => { const assetsDir = path.resolve(testDir, 'dist/es/assets') const files = fs.readdirSync(assetsDir) - expect(files.length).toBe(27) + expect(files.length).toBe(28) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const worker = files.find((f) => f.includes('my-worker')) diff --git a/playground/worker/__tests__/iife/iife-worker.spec.ts b/playground/worker/__tests__/iife/iife-worker.spec.ts index 553159f79bd41a..6a97c8b2023194 100644 --- a/playground/worker/__tests__/iife/iife-worker.spec.ts +++ b/playground/worker/__tests__/iife/iife-worker.spec.ts @@ -10,6 +10,11 @@ test('normal', async () => { () => page.textContent('.bundle-with-plugin'), 'worker bundle with plugin success!' ) + await untilUpdated( + () => page.textContent('.asset-url'), + isBuild ? '/iife/assets/vite.svg' : '/iife/vite.svg', + true + ) }) test('TS output', async () => { @@ -41,7 +46,7 @@ describe.runIf(isBuild)('build', () => { test('inlined code generation', async () => { const assetsDir = path.resolve(testDir, 'dist/iife/assets') const files = fs.readdirSync(assetsDir) - expect(files.length).toBe(15) + expect(files.length).toBe(16) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const worker = files.find((f) => f.includes('my-worker')) diff --git a/playground/worker/__tests__/relative-base/relative-base-worker.spec.ts b/playground/worker/__tests__/relative-base/relative-base-worker.spec.ts index 89c042ba322b27..11d1c185324fa2 100644 --- a/playground/worker/__tests__/relative-base/relative-base-worker.spec.ts +++ b/playground/worker/__tests__/relative-base/relative-base-worker.spec.ts @@ -14,13 +14,19 @@ test('normal', async () => { 'worker bundle with plugin success!', true ) + await untilUpdated( + () => page.textContent('.asset-url'), + isBuild ? '/other-assets/vite' : '/vite.svg', + true + ) }) test('TS output', async () => { await untilUpdated(() => page.textContent('.pong-ts-output'), 'pong', true) }) -test('inlined', async () => { +// TODO: inline worker should inline assets +test.skip('inlined', async () => { await untilUpdated(() => page.textContent('.pong-inline'), 'pong', true) }) @@ -65,7 +71,7 @@ describe.runIf(isBuild)('build', () => { ) // worker should have all imports resolved and no exports - expect(workerContent).not.toMatch(`import`) + expect(workerContent).not.toMatch(/import(?!\.)/) // accept import.meta.url expect(workerContent).not.toMatch(`export`) // chunk expect(content).toMatch(`new Worker(""+new URL("../worker-entries/`) diff --git a/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts b/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts index adb86d4130f25e..80cc7fa63ee900 100644 --- a/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap-hidden/sourcemap-hidden-worker.spec.ts @@ -9,7 +9,7 @@ describe.runIf(isBuild)('build', () => { const files = fs.readdirSync(assetsDir) // should have 2 worker chunk - expect(files.length).toBe(30) + expect(files.length).toBe(31) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const indexSourcemap = getSourceMapUrl(content) diff --git a/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts b/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts index a80b0d9c3f0708..b56bf23f2b3892 100644 --- a/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap-inline/sourcemap-inline-worker.spec.ts @@ -9,7 +9,7 @@ describe.runIf(isBuild)('build', () => { const files = fs.readdirSync(assetsDir) // should have 2 worker chunk - expect(files.length).toBe(15) + expect(files.length).toBe(16) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const indexSourcemap = getSourceMapUrl(content) diff --git a/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts b/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts index a0ad8e7a355b8b..955bf22803ac33 100644 --- a/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts +++ b/playground/worker/__tests__/sourcemap/sourcemap-worker.spec.ts @@ -8,7 +8,7 @@ describe.runIf(isBuild)('build', () => { const assetsDir = path.resolve(testDir, 'dist/iife-sourcemap/assets') const files = fs.readdirSync(assetsDir) // should have 2 worker chunk - expect(files.length).toBe(30) + expect(files.length).toBe(31) const index = files.find((f) => f.includes('main-module')) const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8') const indexSourcemap = getSourceMapUrl(content) diff --git a/playground/worker/index.html b/playground/worker/index.html index d444f2ab878b98..1b196e074d0678 100644 --- a/playground/worker/index.html +++ b/playground/worker/index.html @@ -11,11 +11,13 @@

format iife:

.pong .mode .bundle-with-plugin + .asset-url

Response from worker:
mode:
bundle-with-plugin:
+
asset-url:

diff --git a/playground/worker/my-worker.ts b/playground/worker/my-worker.ts index 9a5203711d3375..f31f081e64d15a 100644 --- a/playground/worker/my-worker.ts +++ b/playground/worker/my-worker.ts @@ -1,13 +1,14 @@ import { msg as msgFromDep } from 'dep-to-optimize' import { mode, msg } from './modules/workerImport' import { bundleWithPlugin } from './modules/test-plugin' +import viteSvg from './vite.svg' self.onmessage = (e) => { if (e.data === 'ping') { - self.postMessage({ msg, mode, bundleWithPlugin }) + self.postMessage({ msg, mode, bundleWithPlugin, viteSvg }) } } -self.postMessage({ msg, mode, bundleWithPlugin, msgFromDep }) +self.postMessage({ msg, mode, bundleWithPlugin, msgFromDep, viteSvg }) // for sourcemap console.log('my-worker.js') diff --git a/playground/worker/vite.svg b/playground/worker/vite.svg new file mode 100644 index 00000000000000..e7b8dfb1b2a60b --- /dev/null +++ b/playground/worker/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/playground/worker/worker/main-module.js b/playground/worker/worker/main-module.js index 4f6fa8dd7b6334..a1205a4a7e46b8 100644 --- a/playground/worker/worker/main-module.js +++ b/playground/worker/worker/main-module.js @@ -17,6 +17,7 @@ worker.addEventListener('message', (e) => { text('.pong', e.data.msg) text('.mode', e.data.mode) text('.bundle-with-plugin', e.data.bundleWithPlugin) + text('.asset-url', e.data.viteSvg) }) const inlineWorker = new InlineWorker()