Skip to content

Commit f05a813

Browse files
authored
fix: import url worker two times (#7468)
1 parent b16b896 commit f05a813

File tree

10 files changed

+181
-50
lines changed

10 files changed

+181
-50
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ if (isBuild) {
6060
// assert correct files
6161
test('inlined code generation', async () => {
6262
const files = fs.readdirSync(assetsDir)
63-
expect(files.length).toBe(20)
63+
expect(files.length).toBe(22)
6464
const index = files.find((f) => f.includes('main-module'))
6565
const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
6666
const worker = files.find((f) => f.includes('my-worker'))
@@ -94,7 +94,7 @@ test('classic worker', async () => {
9494

9595
test('emit chunk', async () => {
9696
expect(await page.textContent('.emti-chunk-worker')).toMatch(
97-
'{"msg1":"module1","msg2":"module2","msg3":"module3"}'
97+
'["A string",{"type":"emit-chunk-sub-worker","data":"A string"},{"type":"module-and-worker:worker","data":"A string"},{"type":"module-and-worker:module","data":"module and worker"},{"type":"emit-chunk-sub-worker","data":{"module":"module and worker","msg1":"module1","msg2":"module2","msg3":"module3"}}]'
9898
)
9999
expect(await page.textContent('.emti-chunk-dynamic-import-worker')).toMatch(
100100
'"A string/es/"'
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
11
import SubWorker from './emit-chunk-sub-worker?worker'
2-
32
const subWorker = new SubWorker()
43

54
subWorker.onmessage = (event) => {
6-
self.postMessage(event.data)
5+
self.postMessage({
6+
type: 'emit-chunk-sub-worker',
7+
data: event.data
8+
})
79
}
10+
11+
const moduleWorker = new Worker(
12+
new URL('./module-and-worker.js', import.meta.url),
13+
{ type: 'module' }
14+
)
15+
16+
moduleWorker.onmessage = (event) => {
17+
self.postMessage({
18+
type: 'module-and-worker:worker',
19+
data: event.data
20+
})
21+
}
22+
23+
import('./module-and-worker').then((res) => {
24+
self.postMessage({
25+
type: 'module-and-worker:module',
26+
data: res.module
27+
})
28+
})
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
Promise.all([import('./modules/module2'), import('./modules/module3')]).then(
2-
(data) => {
3-
const _data = { ...data[0], ...data[1] }
4-
self.postMessage(_data)
5-
}
6-
)
1+
Promise.all([
2+
import('./module-and-worker'),
3+
import('./modules/module2'),
4+
import('./modules/module3')
5+
]).then((data) => {
6+
const _data = { ...data[0], ...data[1], ...data[2] }
7+
self.postMessage(_data)
8+
})

packages/playground/worker/index.html

+9-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ <h2 class="format-iife">format iife:</h2>
6262
<h2 class="format-es"></h2>
6363

6464
<p>
65-
worker emit chunk
65+
worker emit chunk <br />
66+
module and worker:worker in worker file <br />
67+
module and worker:module in worker file <br />
6668
<span class="classname">.emti-chunk-worker</span>
6769
</p>
6870
<code class="emti-chunk-worker"></code>
@@ -73,6 +75,12 @@ <h2 class="format-es"></h2>
7375
</p>
7476
<code class="emti-chunk-dynamic-import-worker"></code>
7577

78+
<p>
79+
module and worker:worker in simple file
80+
<span class="classname">.module-and-worker-worker</span>
81+
</p>
82+
<code class="module-and-worker-worker"></code>
83+
7684
<style>
7785
p {
7886
background: rgba(0, 0, 0, 0.1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import constant from './modules/module'
2+
3+
self.postMessage(constant)
4+
5+
export const module = 'module and worker'

packages/playground/worker/worker/main-format-es.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@ function text(el, text) {
88
text('.format-es', 'format es:')
99

1010
const nestedWorker = new NestedWorker()
11+
const dataList = []
1112
nestedWorker.addEventListener('message', (ev) => {
12-
text('.emti-chunk-worker', JSON.stringify(ev.data))
13+
dataList.push(ev.data)
14+
text(
15+
'.emti-chunk-worker',
16+
JSON.stringify(
17+
dataList.sort(
18+
(a, b) => JSON.stringify(a).length - JSON.stringify(b).length
19+
)
20+
)
21+
)
1322
})
1423

1524
const dynamicImportWorker = new Worker(
@@ -21,3 +30,12 @@ const dynamicImportWorker = new Worker(
2130
dynamicImportWorker.addEventListener('message', (ev) => {
2231
text('.emti-chunk-dynamic-import-worker', JSON.stringify(ev.data))
2332
})
33+
34+
const moduleWorker = new Worker(
35+
new URL('../module-and-worker.js', import.meta.url),
36+
{ type: 'module' }
37+
)
38+
39+
moduleWorker.addEventListener('message', (ev) => {
40+
text('.module-and-worker-worker', JSON.stringify(ev.data))
41+
})

packages/playground/worker/worker/main-module.js

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ inlineWorker.addEventListener('message', (e) => {
2828
})
2929

3030
document.querySelector('.ping-inline').addEventListener('click', () => {
31+
console.log('111')
3132
inlineWorker.postMessage('ping')
3233
})
3334

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

+1-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const isNonJsRequest = (request: string): boolean => nonJsRe.test(request)
1010

1111
export function definePlugin(config: ResolvedConfig): Plugin {
1212
const isBuild = config.command === 'build'
13-
const isWorker = config.isWorker
1413

1514
const processNodeEnv: Record<string, string> = {
1615
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || config.mode),
@@ -41,12 +40,7 @@ export function definePlugin(config: ResolvedConfig): Plugin {
4140
Object.assign(importMetaKeys, {
4241
'import.meta.env.': `({}).`,
4342
'import.meta.env': JSON.stringify(config.env),
44-
'import.meta.hot': `false`,
45-
...(isWorker
46-
? {
47-
'import.meta.url': 'self.location.href'
48-
}
49-
: {})
43+
'import.meta.hot': `false`
5044
})
5145
}
5246

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

+110-17
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,65 @@ import type Rollup from 'rollup'
66
import { ENV_PUBLIC_PATH } from '../constants'
77
import path from 'path'
88
import { onRollupWarning } from '../build'
9+
import type { EmittedFile } from 'rollup'
10+
11+
interface WorkerCache {
12+
// save worker bundle emitted files avoid overwrites the same file.
13+
// <chunk_filename, hash>
14+
assets: Map<string, string>
15+
chunks: Map<string, string>
16+
// worker bundle don't deps on any more worker runtime info an id only had an result.
17+
// save worker bundled file id to avoid repeated execution of bundles
18+
// <filename, hash>
19+
bundle: Map<string, string>
20+
// nested worker bundle context don't had file what emitted by outside bundle
21+
// save the hash to id to rewrite truth id.
22+
// <hash, id>
23+
emitted: Map<string, string>
24+
}
925

1026
const WorkerFileId = 'worker_file'
27+
const workerCache = new WeakMap<ResolvedConfig, WorkerCache>()
28+
29+
function emitWorkerFile(
30+
ctx: Rollup.TransformPluginContext,
31+
config: ResolvedConfig,
32+
asset: EmittedFile,
33+
type: 'assets' | 'chunks'
34+
): string {
35+
const fileName = asset.fileName!
36+
const workerMap = workerCache.get(config)!
37+
38+
if (workerMap[type].has(fileName)) {
39+
return workerMap[type].get(fileName)!
40+
}
41+
const hash = ctx.emitFile(asset)
42+
workerMap[type].set(fileName, hash)
43+
workerMap.emitted.set(hash, fileName)
44+
return hash
45+
}
46+
47+
function emitWorkerAssets(
48+
ctx: Rollup.TransformPluginContext,
49+
config: ResolvedConfig,
50+
asset: EmittedFile
51+
) {
52+
const { format } = config.worker
53+
return emitWorkerFile(
54+
ctx,
55+
config,
56+
asset,
57+
format === 'es' ? 'chunks' : 'assets'
58+
)
59+
}
60+
61+
function emitWorkerChunks(
62+
ctx: Rollup.TransformPluginContext,
63+
config: ResolvedConfig,
64+
asset: EmittedFile
65+
) {
66+
return emitWorkerFile(ctx, config, asset, 'chunks')
67+
}
1168

1269
export async function bundleWorkerEntry(
1370
ctx: Rollup.TransformPluginContext,
@@ -37,11 +94,13 @@ export async function bundleWorkerEntry(
3794
code = outputCode.code
3895
outputChunks.forEach((outputChunk) => {
3996
if (outputChunk.type === 'asset') {
40-
ctx.emitFile(outputChunk)
41-
}
42-
if (outputChunk.type === 'chunk') {
43-
ctx.emitFile({
44-
fileName: `${config.build.assetsDir}/${outputChunk.fileName}`,
97+
emitWorkerAssets(ctx, config, outputChunk)
98+
} else if (outputChunk.type === 'chunk') {
99+
emitWorkerChunks(ctx, config, {
100+
fileName: path.posix.join(
101+
config.build.assetsDir,
102+
outputChunk.fileName
103+
),
45104
source: outputChunk.code,
46105
type: 'asset'
47106
})
@@ -53,12 +112,50 @@ export async function bundleWorkerEntry(
53112
return Buffer.from(code)
54113
}
55114

115+
export async function workerFileToUrl(
116+
ctx: Rollup.TransformPluginContext,
117+
config: ResolvedConfig,
118+
id: string
119+
): Promise<string> {
120+
const workerMap = workerCache.get(config)!
121+
122+
let hash = workerMap.bundle.get(id)
123+
if (hash) {
124+
// rewrite truth id, no need to replace by asset plugin
125+
return config.base + workerMap.emitted.get(hash)!
126+
}
127+
const code = await bundleWorkerEntry(ctx, config, id)
128+
const basename = path.parse(cleanUrl(id)).name
129+
const contentHash = getAssetHash(code)
130+
const fileName = path.posix.join(
131+
config.build.assetsDir,
132+
`${basename}.${contentHash}.js`
133+
)
134+
hash = emitWorkerAssets(ctx, config, {
135+
fileName,
136+
type: 'asset',
137+
source: code
138+
})
139+
workerMap.bundle.set(id, hash)
140+
return `__VITE_ASSET__${hash}__`
141+
}
142+
56143
export function webWorkerPlugin(config: ResolvedConfig): Plugin {
57144
const isBuild = config.command === 'build'
145+
const isWorker = config.isWorker
58146

59147
return {
60148
name: 'vite:worker',
61149

150+
buildStart() {
151+
workerCache.set(config, {
152+
assets: new Map(),
153+
chunks: new Map(),
154+
bundle: new Map(),
155+
emitted: new Map()
156+
})
157+
},
158+
62159
load(id) {
63160
if (isBuild) {
64161
const parsedQuery = parseRequest(id)
@@ -87,8 +184,8 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
87184

88185
let url: string
89186
if (isBuild) {
90-
const code = await bundleWorkerEntry(this, config, id)
91187
if (query.inline != null) {
188+
const code = await bundleWorkerEntry(this, config, id)
92189
const { format } = config.worker
93190
const workerOptions = format === 'es' ? '{type: "module"}' : '{}'
94191
// inline as blob data url
@@ -103,17 +200,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
103200
}
104201
}`
105202
} else {
106-
const basename = path.parse(cleanUrl(id)).name
107-
const contentHash = getAssetHash(code)
108-
const fileName = path.posix.join(
109-
config.build.assetsDir,
110-
`${basename}.${contentHash}.js`
111-
)
112-
url = `__VITE_ASSET__${this.emitFile({
113-
fileName,
114-
type: 'asset',
115-
source: code
116-
})}__`
203+
url = await workerFileToUrl(this, config, id)
117204
}
118205
} else {
119206
url = await fileToUrl(cleanUrl(id), config, this)
@@ -129,6 +216,12 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
129216
url
130217
)}, ${JSON.stringify(workerOptions, null, 2)})
131218
}`
219+
},
220+
221+
renderChunk(code) {
222+
if (isWorker && code.includes('import.meta.url')) {
223+
return code.replace('import.meta.url', 'self.location.href')
224+
}
132225
}
133226
}
134227
}

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

+3-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import JSON5 from 'json5'
22
import type { ResolvedConfig } from '../config'
33
import type { Plugin } from '../plugin'
4-
import { getAssetHash, fileToUrl } from './asset'
4+
import { fileToUrl } from './asset'
55
import {
66
blankReplacer,
77
cleanUrl,
@@ -11,7 +11,7 @@ import {
1111
stringsRE
1212
} from '../utils'
1313
import path from 'path'
14-
import { bundleWorkerEntry } from './worker'
14+
import { workerFileToUrl } from './worker'
1515
import { parseRequest } from '../utils'
1616
import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants'
1717
import MagicString from 'magic-string'
@@ -162,18 +162,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
162162
const file = path.resolve(path.dirname(id), rawUrl.slice(1, -1))
163163
let url: string
164164
if (isBuild) {
165-
const content = await bundleWorkerEntry(this, config, file)
166-
const basename = path.parse(cleanUrl(file)).name
167-
const contentHash = getAssetHash(content)
168-
const fileName = path.posix.join(
169-
config.build.assetsDir,
170-
`${basename}.${contentHash}.js`
171-
)
172-
url = `__VITE_ASSET__${this.emitFile({
173-
fileName,
174-
type: 'asset',
175-
source: content
176-
})}__`
165+
url = await workerFileToUrl(this, config, file)
177166
} else {
178167
url = await fileToUrl(cleanUrl(file), config, this)
179168
url = injectQuery(url, WORKER_FILE_ID)

0 commit comments

Comments
 (0)