forked from vitejs/vite
-
Notifications
You must be signed in to change notification settings - Fork 0
/
worker.ts
134 lines (125 loc) · 4.01 KB
/
worker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import type { ResolvedConfig } from '../config'
import type { Plugin } from '../plugin'
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'
const WorkerFileId = 'worker_file'
export async function bundleWorkerEntry(
ctx: Rollup.TransformPluginContext,
config: ResolvedConfig,
id: string
): Promise<Buffer> {
// bundle the file as entry to support imports
const rollup = require('rollup') as typeof Rollup
const { plugins, rollupOptions, format } = config.worker
const bundle = await rollup.rollup({
...rollupOptions,
input: cleanUrl(id),
plugins,
onwarn(warning, warn) {
onRollupWarning(warning, warn, config)
},
preserveEntrySignatures: false
})
let code: string
try {
const {
output: [outputCode, ...outputChunks]
} = await bundle.generate({
format,
sourcemap: config.build.sourcemap
})
code = outputCode.code
outputChunks.forEach((outputChunk) => {
if (outputChunk.type === 'asset') {
ctx.emitFile(outputChunk)
}
if (outputChunk.type === 'chunk') {
ctx.emitFile({
fileName: `${config.build.assetsDir}/${outputChunk.fileName}`,
source: outputChunk.code,
type: 'asset'
})
}
})
} finally {
await bundle.close()
}
return Buffer.from(code)
}
export function webWorkerPlugin(config: ResolvedConfig): Plugin {
const isBuild = config.command === 'build'
return {
name: 'vite:worker',
load(id) {
if (isBuild) {
const parsedQuery = parseRequest(id)
if (
parsedQuery &&
(parsedQuery.worker ?? parsedQuery.sharedworker) != null
) {
return ''
}
}
},
async transform(_, id) {
const query = parseRequest(id)
if (query && query[WorkerFileId] != null) {
return {
code: `import '${ENV_PUBLIC_PATH}'\n` + _
}
}
if (
query == null ||
(query && (query.worker ?? query.sharedworker) == null)
) {
return
}
let url: string
if (isBuild) {
const code = await bundleWorkerEntry(this, config, id)
if (query.inline != null) {
const { format } = config.worker
const workerOptions = format === 'es' ? '{type: "module"}' : '{}'
// inline as blob data url
return `const encodedJs = "${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);
try {
return objURL ? new Worker(objURL, ${workerOptions}) : new Worker("data:application/javascript;base64," + encodedJs, {type: "module"});
} finally {
objURL && (window.URL || window.webkitURL).revokeObjectURL(objURL);
}
}`
} else {
const basename = path.parse(cleanUrl(id)).name
const contentHash = getAssetHash(code)
const fileName = path.posix.join(
config.build.assetsDir,
`${basename}.${contentHash}.js`
)
url = `__VITE_ASSET__${this.emitFile({
fileName,
type: 'asset',
source: code
})}__`
}
} else {
url = await fileToUrl(cleanUrl(id), config, this)
url = injectQuery(url, WorkerFileId)
}
const workerConstructor =
query.sharedworker != null ? 'SharedWorker' : 'Worker'
const workerOptions = { type: 'module' }
return `export default function WorkerWrapper() {
return new ${workerConstructor}(${JSON.stringify(
url
)}, ${JSON.stringify(workerOptions, null, 2)})
}`
}
}
}