vite
Version:
Native-ESM powered web dev build tool
116 lines (106 loc) • 3.42 kB
text/typescript
import { ResolvedConfig } from '../config'
import { Plugin } from '../plugin'
import { resolvePlugins } from '../plugins'
import { parse as parseUrl } from 'url'
import qs, { ParsedUrlQuery } from 'querystring'
import { fileToUrl, getAssetHash } from './asset'
import { cleanUrl, injectQuery } from '../utils'
import Rollup from 'rollup'
import { ENV_PUBLIC_PATH } from '../constants'
import path from 'path'
function parseWorkerRequest(id: string): ParsedUrlQuery | null {
const { search } = parseUrl(id)
if (!search) {
return null
}
return qs.parse(search.slice(1))
}
const WorkerFileId = 'worker_file'
export function webWorkerPlugin(config: ResolvedConfig): Plugin {
const isBuild = config.command === 'build'
return {
name: 'vite:worker',
load(id) {
if (isBuild) {
const parsedQuery = parseWorkerRequest(id)
if (
parsedQuery &&
(parsedQuery.worker ?? parsedQuery.sharedworker) != null
) {
return ''
}
}
},
async transform(_, id) {
const query = parseWorkerRequest(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) {
// bundle the file as entry to support imports
const rollup = require('rollup') as typeof Rollup
const bundle = await rollup.rollup({
input: cleanUrl(id),
plugins: await resolvePlugins({ ...config }, [], [], [])
})
let code: string
try {
const { output } = await bundle.generate({
format: 'iife',
sourcemap: config.build.sourcemap
})
code = output[0].code
} finally {
await bundle.close()
}
const content = Buffer.from(code)
if (query.inline != null) {
// inline as blob data url
return `const blob = new Blob([atob(\"${content.toString(
'base64'
)}\")], { type: 'text/javascript;charset=utf-8' });
export default function WorkerWrapper() {
const objURL = (window.URL || window.webkitURL).createObjectURL(blob);
try {
return new Worker(objURL);
} finally {
(window.URL || window.webkitURL).revokeObjectURL(objURL);
}
}`
} else {
const basename = path.parse(cleanUrl(id)).name
const contentHash = getAssetHash(content)
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)})
}`
}
}
}