From 5c297e57841dab997f8af64a79967d9a308160c5 Mon Sep 17 00:00:00 2001 From: yoho Date: Mon, 18 Apr 2022 13:50:21 +0800 Subject: [PATCH 1/9] wip: support assetFileNames --- packages/playground/worker/vite.config-es.js | 18 ++- packages/vite/src/node/config.ts | 8 +- packages/vite/src/node/plugins/asset.ts | 2 +- packages/vite/src/node/plugins/worker.ts | 111 +++++++++++-------- 4 files changed, 91 insertions(+), 48 deletions(-) diff --git a/packages/playground/worker/vite.config-es.js b/packages/playground/worker/vite.config-es.js index a65dece2d0db21..846b672a297751 100644 --- a/packages/playground/worker/vite.config-es.js +++ b/packages/playground/worker/vite.config-es.js @@ -7,10 +7,24 @@ module.exports = vite.defineConfig({ enforce: 'pre', worker: { format: 'es', - plugins: [vueJsx()] + plugins: [vueJsx()], + rollupOptions: { + output: { + assetFileNames: 'assets/[name].[hash].[ext]', + chunkFileNames: 'assets/[name].[hash].js', + entryFileNames: 'assets/[name].[hash].js' + } + } }, build: { - outDir: 'dist/es' + outDir: 'dist/es', + rollupOptions: { + output: { + assetFileNames: 'assets/asset.[name].[ext]', + chunkFileNames: 'assets/chunk.[name].js', + entryFileNames: 'assets/entry.[name].js' + } + } }, plugins: [ { diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index b02ac6e496593e..9f702122c19d18 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -251,6 +251,7 @@ export type ResolvedConfig = Readonly< command: 'build' | 'serve' mode: string isWorker: boolean + rawConfig: ResolvedConfig | null isProduction: boolean env: Record resolve: ResolveOptions & { @@ -482,6 +483,7 @@ export async function resolveConfig( command, mode, isWorker: false, + rawConfig: null, isProduction, plugins: userPlugins, server, @@ -515,7 +517,11 @@ export async function resolveConfig( // flat config.worker.plugin const [workerPrePlugins, workerNormalPlugins, workerPostPlugins] = sortUserPlugins(config.worker?.plugins as Plugin[]) - const workerResolved: ResolvedConfig = { ...resolved, isWorker: true } + const workerResolved: ResolvedConfig = { + ...resolved, + isWorker: true, + rawConfig: resolved + } resolved.worker.plugins = await resolvePlugins( workerResolved, workerPrePlugins, diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 633438cf3cb0d4..f2eed2bc28bc5a 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -337,7 +337,7 @@ async function fileToBuiltUrl( return url } -export function getAssetHash(content: Buffer): string { +export function getAssetHash(content: Buffer | string): string { return createHash('sha256').update(content).digest('hex').slice(0, 8) } diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 4113b7153f9b35..387a9bddb0ef62 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -1,53 +1,57 @@ import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' -import { fileToUrl, getAssetHash } from './asset' +import { assetFileNamesToFileName, fileToUrl, getAssetHash } from './asset' import { cleanUrl, injectQuery, parseRequest } from '../utils' import type Rollup from 'rollup' import { ENV_PUBLIC_PATH } from '../constants' import path from 'path' import { onRollupWarning } from '../build' -import type { TransformPluginContext, EmittedFile } from 'rollup' +import type { + TransformPluginContext, + OutputOptions, + EmittedAsset +} from 'rollup' + +interface WorkerEmittedAsset extends EmittedAsset { + hash: string +} interface WorkerCache { // save worker bundle emitted files avoid overwrites the same file. - // - assets: Map - chunks: Map + // + assets: Map + chunks: Map // worker bundle don't deps on any more worker runtime info an id only had an result. // save worker bundled file id to avoid repeated execution of bundles // bundle: Map - // nested worker bundle context don't had file what emitted by outside bundle - // save the hash to id to rewrite truth id. - // - emitted: Map } const WorkerFileId = 'worker_file' const workerCache = new WeakMap() function emitWorkerFile( - ctx: Rollup.TransformPluginContext, + ctx: TransformPluginContext, config: ResolvedConfig, - asset: EmittedFile, + asset: EmittedAsset, type: 'assets' | 'chunks' ): string { const fileName = asset.fileName! - const workerMap = workerCache.get(config)! + const workerMap = workerCache.get(config.rawConfig || config)! if (workerMap[type].has(fileName)) { - return workerMap[type].get(fileName)! + return workerMap[type].get(fileName)!.hash } - const hash = ctx.emitFile(asset) - workerMap[type].set(fileName, hash) - workerMap.emitted.set(hash, fileName) + const hash = getAssetHash(fileName) + ;(asset as WorkerEmittedAsset).hash = hash + workerMap[type].set(fileName, asset as WorkerEmittedAsset) return hash } function emitWorkerAssets( - ctx: Rollup.TransformPluginContext, + ctx: TransformPluginContext, config: ResolvedConfig, - asset: EmittedFile + asset: EmittedAsset ) { const { format } = config.worker return emitWorkerFile( @@ -59,15 +63,24 @@ function emitWorkerAssets( } function emitWorkerChunks( - ctx: Rollup.TransformPluginContext, + ctx: TransformPluginContext, config: ResolvedConfig, - asset: EmittedFile + asset: EmittedAsset ) { return emitWorkerFile(ctx, config, asset, 'chunks') } +function getWorkerOutputFormat(config: ResolvedConfig): OutputOptions { + const workerOutputConfig = config.worker.rollupOptions.output + return workerOutputConfig + ? Array.isArray(workerOutputConfig) + ? workerOutputConfig[0] || {} + : workerOutputConfig + : {} +} + export async function bundleWorkerEntry( - ctx: Rollup.TransformPluginContext, + ctx: TransformPluginContext, config: ResolvedConfig, id: string, query: Record | null @@ -86,22 +99,29 @@ export async function bundleWorkerEntry( }) let chunk: Rollup.OutputChunk try { + const workerConfig = getWorkerOutputFormat(config) const { output: [outputChunk, ...outputChunks] } = await bundle.generate({ + dir: config.build.assetsDir, + assetFileNames: + workerConfig.assetFileNames || + path.posix.join(config.build.assetsDir, '[name].[hash].[ext]'), + chunkFileNames: + workerConfig.chunkFileNames || + path.posix.join(config.build.assetsDir, '[name].[hash].js'), + ...workerConfig, format, sourcemap: config.build.sourcemap }) chunk = outputChunk outputChunks.forEach((outputChunk) => { + // console.log(outputChunk.type, outputChunk.fileName); if (outputChunk.type === 'asset') { emitWorkerAssets(ctx, config, outputChunk) } else if (outputChunk.type === 'chunk') { emitWorkerChunks(ctx, config, { - fileName: path.posix.join( - config.build.assetsDir, - outputChunk.fileName - ), + fileName: outputChunk.fileName, source: outputChunk.code, type: 'asset' }) @@ -169,26 +189,27 @@ export async function workerFileToUrl( id: string, query: Record | null ): Promise { - const workerMap = workerCache.get(config)! + const workerMap = workerCache.get(config.rawConfig || config)! let hash = workerMap.bundle.get(id) - if (hash) { - // rewrite truth id, no need to replace by asset plugin - return config.base + workerMap.emitted.get(hash)! + if (!hash) { + const code = await bundleWorkerEntry(ctx, config, id, query) + const basename = path.parse(cleanUrl(id)).name + const contentHash = getAssetHash(code) + const fileName = assetFileNamesToFileName( + getWorkerOutputFormat(config).assetFileNames || + path.posix.join(config.build.assetsDir, '[name].[hash].[ext]'), + `${basename}.js`, + contentHash, + code + ) + hash = emitWorkerAssets(ctx, config, { + fileName, + type: 'asset', + source: code + }) + workerMap.bundle.set(id, hash) } - const code = await bundleWorkerEntry(ctx, config, id, query) - const basename = path.parse(cleanUrl(id)).name - const contentHash = getAssetHash(code) - const fileName = path.posix.join( - config.build.assetsDir, - `${basename}.${contentHash}.js` - ) - hash = emitWorkerAssets(ctx, config, { - fileName, - type: 'asset', - source: code - }) - workerMap.bundle.set(id, hash) return `__VITE_ASSET__${hash}__` } @@ -200,11 +221,13 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { name: 'vite:worker', buildStart() { + if (isWorker) { + return + } workerCache.set(config, { assets: new Map(), chunks: new Map(), - bundle: new Map(), - emitted: new Map() + bundle: new Map() }) }, From 2f87b6544bcb7f557298c12101b81ce1f0f545df Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 19 Apr 2022 00:08:38 +0800 Subject: [PATCH 2/9] wip: nested worker emit file --- packages/playground/worker/vite.config-es.js | 12 +-- packages/vite/src/node/plugins/worker.ts | 103 ++++++++----------- 2 files changed, 47 insertions(+), 68 deletions(-) diff --git a/packages/playground/worker/vite.config-es.js b/packages/playground/worker/vite.config-es.js index 846b672a297751..a799f46649b446 100644 --- a/packages/playground/worker/vite.config-es.js +++ b/packages/playground/worker/vite.config-es.js @@ -10,9 +10,9 @@ module.exports = vite.defineConfig({ plugins: [vueJsx()], rollupOptions: { output: { - assetFileNames: 'assets/[name].[hash].[ext]', - chunkFileNames: 'assets/[name].[hash].js', - entryFileNames: 'assets/[name].[hash].js' + // assetFileNames: 'assets/worker_asset.[name].[ext]', + // chunkFileNames: 'assets/worker_chunk.[name].js', + // entryFileNames: 'assets/[name].js' } } }, @@ -20,9 +20,9 @@ module.exports = vite.defineConfig({ outDir: 'dist/es', rollupOptions: { output: { - assetFileNames: 'assets/asset.[name].[ext]', - chunkFileNames: 'assets/chunk.[name].js', - entryFileNames: 'assets/entry.[name].js' + assetFileNames: 'assets/[name].[ext]', + chunkFileNames: 'assets/[name].js', + entryFileNames: 'assets/[name].js' } } }, diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 387a9bddb0ef62..363083d2a2d83d 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -12,64 +12,32 @@ import type { EmittedAsset } from 'rollup' -interface WorkerEmittedAsset extends EmittedAsset { - hash: string -} - interface WorkerCache { - // save worker bundle emitted files avoid overwrites the same file. - // - assets: Map - chunks: Map + // let the worker chunks all emit in the top-level emitFile + // + chunks: Map + // worker bundle don't deps on any more worker runtime info an id only had an result. // save worker bundled file id to avoid repeated execution of bundles - // + // bundle: Map } const WorkerFileId = 'worker_file' const workerCache = new WeakMap() -function emitWorkerFile( - ctx: TransformPluginContext, - config: ResolvedConfig, - asset: EmittedAsset, - type: 'assets' | 'chunks' -): string { +function emitWorkerChunks(config: ResolvedConfig, asset: EmittedAsset): string { const fileName = asset.fileName! const workerMap = workerCache.get(config.rawConfig || config)! - - if (workerMap[type].has(fileName)) { - return workerMap[type].get(fileName)!.hash + if (workerMap.chunks.has(fileName)) { + return (workerMap.chunks.get(fileName)! as any).hash } const hash = getAssetHash(fileName) - ;(asset as WorkerEmittedAsset).hash = hash - workerMap[type].set(fileName, asset as WorkerEmittedAsset) + ;(asset as any).hash = hash + workerMap.chunks.set(fileName, asset) return hash } -function emitWorkerAssets( - ctx: TransformPluginContext, - config: ResolvedConfig, - asset: EmittedAsset -) { - const { format } = config.worker - return emitWorkerFile( - ctx, - config, - asset, - format === 'es' ? 'chunks' : 'assets' - ) -} - -function emitWorkerChunks( - ctx: TransformPluginContext, - config: ResolvedConfig, - asset: EmittedAsset -) { - return emitWorkerFile(ctx, config, asset, 'chunks') -} - function getWorkerOutputFormat(config: ResolvedConfig): OutputOptions { const workerOutputConfig = config.worker.rollupOptions.output return workerOutputConfig @@ -79,6 +47,11 @@ function getWorkerOutputFormat(config: ResolvedConfig): OutputOptions { : {} } +// Nested worker construction is a recursive process. The outputChunk of asset type can be output directly. +// But the outputChunk of the chunk type needs to use the asset type to emitFile, +// which will cause it to become an asset in the recursive process. +// In a recursive process Rollup avoids the asset and chunk to add count to the name of the outputChunk generated later. +// will generate the exact same file (file.js / file2.js) So we let the worker chunks all emit in the top-level emitFile export async function bundleWorkerEntry( ctx: TransformPluginContext, config: ResolvedConfig, @@ -103,30 +76,38 @@ export async function bundleWorkerEntry( const { output: [outputChunk, ...outputChunks] } = await bundle.generate({ - dir: config.build.assetsDir, - assetFileNames: - workerConfig.assetFileNames || - path.posix.join(config.build.assetsDir, '[name].[hash].[ext]'), - chunkFileNames: - workerConfig.chunkFileNames || - path.posix.join(config.build.assetsDir, '[name].[hash].js'), - ...workerConfig, + assetFileNames: path.posix.join( + config.build.assetsDir, + '[name].[hash].[ext]' + ), + chunkFileNames: path.posix.join( + config.build.assetsDir, + '[name].[hash].js' + ), + dir: path.join(config.root, config.build.outDir, config.build.assetsDir), format, - sourcemap: config.build.sourcemap + sourcemap: config.build.sourcemap, + ...workerConfig }) chunk = outputChunk outputChunks.forEach((outputChunk) => { - // console.log(outputChunk.type, outputChunk.fileName); if (outputChunk.type === 'asset') { - emitWorkerAssets(ctx, config, outputChunk) + ctx.emitFile(outputChunk) } else if (outputChunk.type === 'chunk') { - emitWorkerChunks(ctx, config, { + emitWorkerChunks(config, { fileName: outputChunk.fileName, source: outputChunk.code, type: 'asset' }) } }) + if (!config.isWorker) { + const workerMap = workerCache.get(config.rawConfig || config)! + workerMap.chunks.forEach((asset) => { + ctx.emitFile(asset) + }) + workerMap.chunks.clear() + } } finally { await bundle.close() } @@ -190,27 +171,26 @@ export async function workerFileToUrl( query: Record | null ): Promise { const workerMap = workerCache.get(config.rawConfig || config)! - - let hash = workerMap.bundle.get(id) - if (!hash) { + let fileName = workerMap.bundle.get(id) + if (!fileName) { const code = await bundleWorkerEntry(ctx, config, id, query) const basename = path.parse(cleanUrl(id)).name const contentHash = getAssetHash(code) - const fileName = assetFileNamesToFileName( + fileName = assetFileNamesToFileName( getWorkerOutputFormat(config).assetFileNames || path.posix.join(config.build.assetsDir, '[name].[hash].[ext]'), `${basename}.js`, contentHash, code ) - hash = emitWorkerAssets(ctx, config, { + ctx.emitFile({ fileName, type: 'asset', source: code }) - workerMap.bundle.set(id, hash) + workerMap.bundle.set(id, fileName) } - return `__VITE_ASSET__${hash}__` + return fileName } export function webWorkerPlugin(config: ResolvedConfig): Plugin { @@ -225,7 +205,6 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { return } workerCache.set(config, { - assets: new Map(), chunks: new Map(), bundle: new Map() }) From 2920e5863cdaf4d6958bfafe4a9a8ecfb92151fa Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 19 Apr 2022 19:48:09 +0800 Subject: [PATCH 3/9] fix: es import relative path --- packages/playground/worker/vite.config-es.js | 6 +- packages/vite/src/node/plugins/worker.ts | 74 +++++++++----------- 2 files changed, 35 insertions(+), 45 deletions(-) diff --git a/packages/playground/worker/vite.config-es.js b/packages/playground/worker/vite.config-es.js index a799f46649b446..899ce6d0825a8b 100644 --- a/packages/playground/worker/vite.config-es.js +++ b/packages/playground/worker/vite.config-es.js @@ -10,9 +10,9 @@ module.exports = vite.defineConfig({ plugins: [vueJsx()], rollupOptions: { output: { - // assetFileNames: 'assets/worker_asset.[name].[ext]', - // chunkFileNames: 'assets/worker_chunk.[name].js', - // entryFileNames: 'assets/[name].js' + assetFileNames: 'assets/worker_asset.[name].[ext]', + chunkFileNames: 'assets/worker_chunk.[name].js', + entryFileNames: 'assets/worker_entry.[name].js' } } }, diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 363083d2a2d83d..49fe6b494af899 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -1,16 +1,12 @@ import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' -import { assetFileNamesToFileName, fileToUrl, getAssetHash } from './asset' +import { fileToUrl, getAssetHash } from './asset' import { cleanUrl, injectQuery, parseRequest } from '../utils' import type Rollup from 'rollup' import { ENV_PUBLIC_PATH } from '../constants' import path from 'path' import { onRollupWarning } from '../build' -import type { - TransformPluginContext, - OutputOptions, - EmittedAsset -} from 'rollup' +import type { TransformPluginContext, EmittedAsset } from 'rollup' interface WorkerCache { // let the worker chunks all emit in the top-level emitFile @@ -38,15 +34,6 @@ function emitWorkerChunks(config: ResolvedConfig, asset: EmittedAsset): string { return hash } -function getWorkerOutputFormat(config: ResolvedConfig): OutputOptions { - const workerOutputConfig = config.worker.rollupOptions.output - return workerOutputConfig - ? Array.isArray(workerOutputConfig) - ? workerOutputConfig[0] || {} - : workerOutputConfig - : {} -} - // Nested worker construction is a recursive process. The outputChunk of asset type can be output directly. // But the outputChunk of the chunk type needs to use the asset type to emitFile, // which will cause it to become an asset in the recursive process. @@ -57,7 +44,7 @@ export async function bundleWorkerEntry( config: ResolvedConfig, id: string, query: Record | null -): Promise { +): Promise { // bundle the file as entry to support imports const rollup = require('rollup') as typeof Rollup const { plugins, rollupOptions, format } = config.worker @@ -72,22 +59,30 @@ export async function bundleWorkerEntry( }) let chunk: Rollup.OutputChunk try { - const workerConfig = getWorkerOutputFormat(config) + const workerOutputConfig = config.worker.rollupOptions.output + const workerConfig = workerOutputConfig + ? Array.isArray(workerOutputConfig) + ? workerOutputConfig[0] || {} + : workerOutputConfig + : {} const { output: [outputChunk, ...outputChunks] } = await bundle.generate({ - assetFileNames: path.posix.join( + entryFileNames: path.posix.join( config.build.assetsDir, - '[name].[hash].[ext]' + '[name].[hash].js' ), chunkFileNames: path.posix.join( config.build.assetsDir, '[name].[hash].js' ), - dir: path.join(config.root, config.build.outDir, config.build.assetsDir), + assetFileNames: path.posix.join( + config.build.assetsDir, + '[name].[hash].[ext]' + ), + ...workerConfig, format, - sourcemap: config.build.sourcemap, - ...workerConfig + sourcemap: config.build.sourcemap }) chunk = outputChunk outputChunks.forEach((outputChunk) => { @@ -120,15 +115,16 @@ function emitSourcemapForWorkerEntry( id: string, query: Record | null, chunk: Rollup.OutputChunk -): Buffer { - let { code, map: sourcemap } = chunk +): Rollup.OutputChunk { + const { map: sourcemap } = chunk + if (sourcemap) { if (config.build.sourcemap === 'inline') { // Manually add the sourcemap to the code if configured for inline sourcemaps. // TODO: Remove when https://github.com/rollup/rollup/issues/3913 is resolved // Currently seems that it won't be resolved until Rollup 3 const dataUrl = sourcemap.toUrl() - code += `//# sourceMappingURL=${dataUrl}` + chunk.code += `//# sourceMappingURL=${dataUrl}` } else if ( config.build.sourcemap === 'hidden' || config.build.sourcemap === true @@ -156,12 +152,12 @@ function emitSourcemapForWorkerEntry( // inline web workers need to use the full sourcemap path // non-inline web workers can use a relative path const sourceMapUrl = query?.inline != null ? filePath : fileName - code += `//# sourceMappingURL=${sourceMapUrl}` + chunk.code += `//# sourceMappingURL=${sourceMapUrl}` } } } - return Buffer.from(code) + return chunk } export async function workerFileToUrl( @@ -173,24 +169,16 @@ export async function workerFileToUrl( const workerMap = workerCache.get(config.rawConfig || config)! let fileName = workerMap.bundle.get(id) if (!fileName) { - const code = await bundleWorkerEntry(ctx, config, id, query) - const basename = path.parse(cleanUrl(id)).name - const contentHash = getAssetHash(code) - fileName = assetFileNamesToFileName( - getWorkerOutputFormat(config).assetFileNames || - path.posix.join(config.build.assetsDir, '[name].[hash].[ext]'), - `${basename}.js`, - contentHash, - code - ) + const outputChunk = await bundleWorkerEntry(ctx, config, id, query) + fileName = outputChunk.fileName ctx.emitFile({ fileName, - type: 'asset', - source: code + source: outputChunk.code, + type: 'asset' }) workerMap.bundle.set(id, fileName) } - return fileName + return config.base + fileName } export function webWorkerPlugin(config: ResolvedConfig): Plugin { @@ -239,12 +227,14 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { let url: string if (isBuild) { if (query.inline != null) { - const code = await bundleWorkerEntry(this, config, id, query) + const chunk = await bundleWorkerEntry(this, config, id, query) const { format } = config.worker const workerOptions = format === 'es' ? '{type: "module"}' : '{}' // inline as blob data url return { - code: `const encodedJs = "${code.toString('base64')}"; + code: `const encodedJs = "${Buffer.from(chunk.code).toString( + 'base64' + )}"; const blob = typeof window !== "undefined" && window.Blob && new Blob([atob(encodedJs)], { type: "text/javascript;charset=utf-8" }); export default function WorkerWrapper() { const objURL = blob && (window.URL || window.webkitURL).createObjectURL(blob); From 47a41501095ac6b97b37eb7b049cff7dcb21bac8 Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 19 Apr 2022 20:12:42 +0800 Subject: [PATCH 4/9] fix: useless chunk cache --- packages/vite/src/node/plugins/worker.ts | 28 ++---------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 49fe6b494af899..0b5908551c6d87 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -6,13 +6,9 @@ import type Rollup from 'rollup' import { ENV_PUBLIC_PATH } from '../constants' import path from 'path' import { onRollupWarning } from '../build' -import type { TransformPluginContext, EmittedAsset } from 'rollup' +import type { TransformPluginContext } from 'rollup' interface WorkerCache { - // let the worker chunks all emit in the top-level emitFile - // - chunks: Map - // worker bundle don't deps on any more worker runtime info an id only had an result. // save worker bundled file id to avoid repeated execution of bundles // @@ -22,18 +18,6 @@ interface WorkerCache { const WorkerFileId = 'worker_file' const workerCache = new WeakMap() -function emitWorkerChunks(config: ResolvedConfig, asset: EmittedAsset): string { - const fileName = asset.fileName! - const workerMap = workerCache.get(config.rawConfig || config)! - if (workerMap.chunks.has(fileName)) { - return (workerMap.chunks.get(fileName)! as any).hash - } - const hash = getAssetHash(fileName) - ;(asset as any).hash = hash - workerMap.chunks.set(fileName, asset) - return hash -} - // Nested worker construction is a recursive process. The outputChunk of asset type can be output directly. // But the outputChunk of the chunk type needs to use the asset type to emitFile, // which will cause it to become an asset in the recursive process. @@ -89,20 +73,13 @@ export async function bundleWorkerEntry( if (outputChunk.type === 'asset') { ctx.emitFile(outputChunk) } else if (outputChunk.type === 'chunk') { - emitWorkerChunks(config, { + ctx.emitFile({ fileName: outputChunk.fileName, source: outputChunk.code, type: 'asset' }) } }) - if (!config.isWorker) { - const workerMap = workerCache.get(config.rawConfig || config)! - workerMap.chunks.forEach((asset) => { - ctx.emitFile(asset) - }) - workerMap.chunks.clear() - } } finally { await bundle.close() } @@ -193,7 +170,6 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { return } workerCache.set(config, { - chunks: new Map(), bundle: new Map() }) }, From 46f056fa6d0c879d70c0d761b6b309c4ce964a62 Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 19 Apr 2022 20:21:00 +0800 Subject: [PATCH 5/9] fix: sourcemap named --- packages/vite/src/node/plugins/worker.ts | 29 +++++++++--------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 0b5908551c6d87..3ae5f63f547ced 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -1,6 +1,6 @@ import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' -import { fileToUrl, getAssetHash } from './asset' +import { fileToUrl } from './asset' import { cleanUrl, injectQuery, parseRequest } from '../utils' import type Rollup from 'rollup' import { ENV_PUBLIC_PATH } from '../constants' @@ -83,13 +83,12 @@ export async function bundleWorkerEntry( } finally { await bundle.close() } - return emitSourcemapForWorkerEntry(ctx, config, id, query, chunk) + return emitSourcemapForWorkerEntry(ctx, config, query, chunk) } function emitSourcemapForWorkerEntry( - context: TransformPluginContext, + ctx: TransformPluginContext, config: ResolvedConfig, - id: string, query: Record | null, chunk: Rollup.OutputChunk ): Rollup.OutputChunk { @@ -106,20 +105,13 @@ function emitSourcemapForWorkerEntry( config.build.sourcemap === 'hidden' || config.build.sourcemap === true ) { - const basename = path.parse(cleanUrl(id)).name const data = sourcemap.toString() - const content = Buffer.from(data) - const contentHash = getAssetHash(content) - const fileName = `${basename}.${contentHash}.js.map` - const filePath = path.posix.join(config.build.assetsDir, fileName) - if (!context.cache.has(contentHash)) { - context.cache.set(contentHash, true) - context.emitFile({ - fileName: filePath, - type: 'asset', - source: data - }) - } + const mapFileName = chunk.fileName + '.map' + ctx.emitFile({ + fileName: mapFileName, + type: 'asset', + source: data + }) // Emit the comment that tells the JS debugger where it can find the // sourcemap file. @@ -128,7 +120,8 @@ function emitSourcemapForWorkerEntry( if (config.build.sourcemap === true) { // inline web workers need to use the full sourcemap path // non-inline web workers can use a relative path - const sourceMapUrl = query?.inline != null ? filePath : fileName + const sourceMapUrl = + query?.inline != null ? mapFileName : chunk.fileName chunk.code += `//# sourceMappingURL=${sourceMapUrl}` } } From 0a5efb772e37bec2c174591b6d9181114e1453d4 Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 19 Apr 2022 20:52:26 +0800 Subject: [PATCH 6/9] Revert "fix: useless chunk cache" This reverts commit 47a41501095ac6b97b37eb7b049cff7dcb21bac8. --- packages/vite/src/node/plugins/worker.ts | 28 ++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 3ae5f63f547ced..de5882438744c0 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -6,9 +6,13 @@ import type Rollup from 'rollup' import { ENV_PUBLIC_PATH } from '../constants' import path from 'path' import { onRollupWarning } from '../build' -import type { TransformPluginContext } from 'rollup' +import type { TransformPluginContext, EmittedAsset } from 'rollup' interface WorkerCache { + // let the worker chunks all emit in the top-level emitFile + // + chunks: Map + // worker bundle don't deps on any more worker runtime info an id only had an result. // save worker bundled file id to avoid repeated execution of bundles // @@ -18,6 +22,18 @@ interface WorkerCache { const WorkerFileId = 'worker_file' const workerCache = new WeakMap() +function emitWorkerChunks(config: ResolvedConfig, asset: EmittedAsset): string { + const fileName = asset.fileName! + const workerMap = workerCache.get(config.rawConfig || config)! + if (workerMap.chunks.has(fileName)) { + return (workerMap.chunks.get(fileName)! as any).hash + } + const hash = getAssetHash(fileName) + ;(asset as any).hash = hash + workerMap.chunks.set(fileName, asset) + return hash +} + // Nested worker construction is a recursive process. The outputChunk of asset type can be output directly. // But the outputChunk of the chunk type needs to use the asset type to emitFile, // which will cause it to become an asset in the recursive process. @@ -73,13 +89,20 @@ export async function bundleWorkerEntry( if (outputChunk.type === 'asset') { ctx.emitFile(outputChunk) } else if (outputChunk.type === 'chunk') { - ctx.emitFile({ + emitWorkerChunks(config, { fileName: outputChunk.fileName, source: outputChunk.code, type: 'asset' }) } }) + if (!config.isWorker) { + const workerMap = workerCache.get(config.rawConfig || config)! + workerMap.chunks.forEach((asset) => { + ctx.emitFile(asset) + }) + workerMap.chunks.clear() + } } finally { await bundle.close() } @@ -163,6 +186,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { return } workerCache.set(config, { + chunks: new Map(), bundle: new Map() }) }, From 31b2f9d86335f91186cb2d5b6d6364a6bb938d56 Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 19 Apr 2022 21:33:28 +0800 Subject: [PATCH 7/9] feat: avoid rollup make the same asset unique --- packages/vite/src/node/plugins/worker.ts | 51 ++++++++++-------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index de5882438744c0..811f8ee0b1e0dd 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -9,9 +9,8 @@ import { onRollupWarning } from '../build' import type { TransformPluginContext, EmittedAsset } from 'rollup' interface WorkerCache { - // let the worker chunks all emit in the top-level emitFile - // - chunks: Map + // save worker all emit chunk avoid rollup make the same asset unique. + assets: Map // worker bundle don't deps on any more worker runtime info an id only had an result. // save worker bundled file id to avoid repeated execution of bundles @@ -22,23 +21,15 @@ interface WorkerCache { const WorkerFileId = 'worker_file' const workerCache = new WeakMap() -function emitWorkerChunks(config: ResolvedConfig, asset: EmittedAsset): string { +function saveEmitWorkerAsset( + config: ResolvedConfig, + asset: EmittedAsset +): void { const fileName = asset.fileName! const workerMap = workerCache.get(config.rawConfig || config)! - if (workerMap.chunks.has(fileName)) { - return (workerMap.chunks.get(fileName)! as any).hash - } - const hash = getAssetHash(fileName) - ;(asset as any).hash = hash - workerMap.chunks.set(fileName, asset) - return hash + workerMap.assets.set(fileName, asset) } -// Nested worker construction is a recursive process. The outputChunk of asset type can be output directly. -// But the outputChunk of the chunk type needs to use the asset type to emitFile, -// which will cause it to become an asset in the recursive process. -// In a recursive process Rollup avoids the asset and chunk to add count to the name of the outputChunk generated later. -// will generate the exact same file (file.js / file2.js) So we let the worker chunks all emit in the top-level emitFile export async function bundleWorkerEntry( ctx: TransformPluginContext, config: ResolvedConfig, @@ -87,22 +78,15 @@ export async function bundleWorkerEntry( chunk = outputChunk outputChunks.forEach((outputChunk) => { if (outputChunk.type === 'asset') { - ctx.emitFile(outputChunk) + saveEmitWorkerAsset(config, outputChunk) } else if (outputChunk.type === 'chunk') { - emitWorkerChunks(config, { + saveEmitWorkerAsset(config, { fileName: outputChunk.fileName, source: outputChunk.code, type: 'asset' }) } }) - if (!config.isWorker) { - const workerMap = workerCache.get(config.rawConfig || config)! - workerMap.chunks.forEach((asset) => { - ctx.emitFile(asset) - }) - workerMap.chunks.clear() - } } finally { await bundle.close() } @@ -130,7 +114,7 @@ function emitSourcemapForWorkerEntry( ) { const data = sourcemap.toString() const mapFileName = chunk.fileName + '.map' - ctx.emitFile({ + saveEmitWorkerAsset(config, { fileName: mapFileName, type: 'asset', source: data @@ -144,7 +128,9 @@ function emitSourcemapForWorkerEntry( // inline web workers need to use the full sourcemap path // non-inline web workers can use a relative path const sourceMapUrl = - query?.inline != null ? mapFileName : chunk.fileName + query?.inline != null + ? mapFileName + : path.relative(config.build.assetsDir, mapFileName) chunk.code += `//# sourceMappingURL=${sourceMapUrl}` } } @@ -164,7 +150,7 @@ export async function workerFileToUrl( if (!fileName) { const outputChunk = await bundleWorkerEntry(ctx, config, id, query) fileName = outputChunk.fileName - ctx.emitFile({ + saveEmitWorkerAsset(config, { fileName, source: outputChunk.code, type: 'asset' @@ -186,7 +172,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { return } workerCache.set(config, { - chunks: new Map(), + assets: new Map(), bundle: new Map() }) }, @@ -267,6 +253,13 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { if (isWorker && code.includes('import.meta.url')) { return code.replace('import.meta.url', 'self.location.href') } + if (!isWorker) { + const workerMap = workerCache.get(config)! + workerMap.assets.forEach((asset) => { + this.emitFile(asset) + workerMap.assets.delete(asset.fileName!) + }) + } } } } From 0ab57c446ebf46470885d6e2e113b7be8ce76fa5 Mon Sep 17 00:00:00 2001 From: yoho Date: Wed, 27 Apr 2022 21:47:28 +0800 Subject: [PATCH 8/9] fix: test --- packages/playground/worker/__tests__/es/es-worker.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/playground/worker/__tests__/es/es-worker.spec.ts b/packages/playground/worker/__tests__/es/es-worker.spec.ts index 27f81b010df74f..08c98db204daa1 100644 --- a/packages/playground/worker/__tests__/es/es-worker.spec.ts +++ b/packages/playground/worker/__tests__/es/es-worker.spec.ts @@ -52,8 +52,10 @@ test.concurrent.each([[true], [false]])('shared worker', async (doTick) => { }) test('worker emitted and import.meta.url in nested worker (serve)', async () => { - expect(await page.textContent('.nested-worker')).toMatch('/worker-nested') - expect(await page.textContent('.nested-worker-module')).toMatch('/sub-worker') + expect(await page.textContent('.nested-worker')).toMatch( + 'worker-nested-worker' + ) + expect(await page.textContent('.nested-worker-module')).toMatch('sub-worker') expect(await page.textContent('.nested-worker-constructor')).toMatch( '"type":"constructor"' ) @@ -64,7 +66,7 @@ if (isBuild) { // assert correct files test('inlined code generation', async () => { const files = fs.readdirSync(assetsDir) - expect(files.length).toBe(26) + expect(files.length).toBe(25) 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')) From cd7386eb3248263f43515da7e1a4266f629aa3b7 Mon Sep 17 00:00:00 2001 From: yoho Date: Tue, 10 May 2022 20:19:19 +0800 Subject: [PATCH 9/9] chore: nice named --- packages/vite/src/node/config.ts | 7 ++++--- packages/vite/src/node/plugins/worker.ts | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 3bb0ba8163dd75..9f1813feaa7c18 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -253,7 +253,8 @@ export type ResolvedConfig = Readonly< command: 'build' | 'serve' mode: string isWorker: boolean - rawConfig: ResolvedConfig | null + /** @internal */ + mainConfig: ResolvedConfig | null isProduction: boolean env: Record resolve: ResolveOptions & { @@ -484,7 +485,7 @@ export async function resolveConfig( command, mode, isWorker: false, - rawConfig: null, + mainConfig: null, isProduction, plugins: userPlugins, server, @@ -519,7 +520,7 @@ export async function resolveConfig( const workerResolved: ResolvedConfig = { ...resolved, isWorker: true, - rawConfig: resolved + mainConfig: resolved } resolved.worker.plugins = await resolvePlugins( workerResolved, diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 893a47c49d9870..c632d5790cbd06 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -26,7 +26,7 @@ function saveEmitWorkerAsset( asset: EmittedAsset ): void { const fileName = asset.fileName! - const workerMap = workerCache.get(config.rawConfig || config)! + const workerMap = workerCache.get(config.mainConfig || config)! workerMap.assets.set(fileName, asset) } @@ -145,7 +145,7 @@ export async function workerFileToUrl( id: string, query: Record | null ): Promise { - const workerMap = workerCache.get(config.rawConfig || config)! + const workerMap = workerCache.get(config.mainConfig || config)! let fileName = workerMap.bundle.get(id) if (!fileName) { const outputChunk = await bundleWorkerEntry(ctx, config, id, query)