Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

worker filterable #52

Merged
merged 1 commit into from
Sep 13, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 100 additions & 85 deletions packages/vite/src/node/plugins/worker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path'
import MagicString from 'magic-string'
import type { OutputChunk } from 'rolldown'
import type { OutputChunk, RolldownPlugin } from 'rolldown'
import type { ResolvedConfig } from '../config'
import type { Plugin } from '../plugin'
import type { ViteDevServer } from '../server'
Expand Down Expand Up @@ -209,14 +209,15 @@ export function webWorkerPostPlugin(): Plugin {
}
}

export function webWorkerPlugin(config: ResolvedConfig): Plugin {
export function webWorkerPlugin(config: ResolvedConfig): RolldownPlugin {
const isBuild = config.command === 'build'
let server: ViteDevServer
const isWorker = config.isWorker

return {
name: 'vite:worker',

// @ts-expect-error TODO: compatible with Vite Plugin
configureServer(_server) {
server = _server
},
Expand All @@ -232,10 +233,17 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
})
},

load(id) {
if (isBuild && workerOrSharedWorkerRE.test(id)) {
return ''
}
load: {
filter: {
id: {
include: [workerOrSharedWorkerRE],
},
},
handler(id) {
if (isBuild && workerOrSharedWorkerRE.test(id)) {
return ''
}
},
},

// TODO @underfin it's not unsupported yet
Expand All @@ -245,74 +253,80 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
// }
// },

async transform(raw, id) {
const workerFileMatch = workerFileRE.exec(id)
if (workerFileMatch) {
// if import worker by worker constructor will have query.type
// other type will be import worker by esm
const workerType = workerFileMatch[1] as WorkerType
let injectEnv = ''

const scriptPath = JSON.stringify(
path.posix.join(config.base, ENV_PUBLIC_PATH),
)
transform: {
filter: {
id: {
include: [workerOrSharedWorkerRE, workerFileRE],
},
},
async handler(raw, id) {
const workerFileMatch = workerFileRE.exec(id)
if (workerFileMatch) {
// if import worker by worker constructor will have query.type
// other type will be import worker by esm
const workerType = workerFileMatch[1] as WorkerType
let injectEnv = ''

const scriptPath = JSON.stringify(
path.posix.join(config.base, ENV_PUBLIC_PATH),
)

if (workerType === 'classic') {
injectEnv = `importScripts(${scriptPath})\n`
} else if (workerType === 'module') {
injectEnv = `import ${scriptPath}\n`
} else if (workerType === 'ignore') {
if (isBuild) {
injectEnv = ''
} else if (server) {
// dynamic worker type we can't know how import the env
// so we copy /@vite/env code of server transform result into file header
const { moduleGraph } = server
const module = moduleGraph.getModuleById(ENV_ENTRY)
injectEnv = module?.transformResult?.code || ''
if (workerType === 'classic') {
injectEnv = `importScripts(${scriptPath})\n`
} else if (workerType === 'module') {
injectEnv = `import ${scriptPath}\n`
} else if (workerType === 'ignore') {
if (isBuild) {
injectEnv = ''
} else if (server) {
// dynamic worker type we can't know how import the env
// so we copy /@vite/env code of server transform result into file header
const { moduleGraph } = server
const module = moduleGraph.getModuleById(ENV_ENTRY)
injectEnv = module?.transformResult?.code || ''
}
}
}
if (injectEnv) {
const s = new MagicString(raw)
s.prepend(injectEnv + ';\n')
return {
code: s.toString(),
map: s.generateMap({ hires: 'boundary' }),
if (injectEnv) {
const s = new MagicString(raw)
s.prepend(injectEnv + ';\n')
return {
code: s.toString(),
map: s.generateMap({ hires: 'boundary' }),
}
}
return
}
return
}

const workerMatch = workerOrSharedWorkerRE.exec(id)
if (!workerMatch) return

const { format } = config.worker
const workerConstructor =
workerMatch[1] === 'sharedworker' ? 'SharedWorker' : 'Worker'
const workerType = isBuild
? format === 'es'
? 'module'
: 'classic'
: 'module'
const workerTypeOption = `{
const workerMatch = workerOrSharedWorkerRE.exec(id)
if (!workerMatch) return

const { format } = config.worker
const workerConstructor =
workerMatch[1] === 'sharedworker' ? 'SharedWorker' : 'Worker'
const workerType = isBuild
? format === 'es'
? 'module'
: 'classic'
: 'module'
const workerTypeOption = `{
${workerType === 'module' ? `type: "module",` : ''}
name: options?.name
}`

let urlCode: string
if (isBuild) {
if (isWorker && config.bundleChain.at(-1) === cleanUrl(id)) {
urlCode = 'self.location.href'
} else if (inlineRE.test(id)) {
const chunk = await bundleWorkerEntry(config, id)
const encodedJs = `const encodedJs = "${Buffer.from(
chunk.code,
).toString('base64')}";`

const code =
// Using blob URL for SharedWorker results in multiple instances of a same worker
workerConstructor === 'Worker'
? `${encodedJs}
let urlCode: string
if (isBuild) {
if (isWorker && config.bundleChain.at(-1) === cleanUrl(id)) {
urlCode = 'self.location.href'
} else if (inlineRE.test(id)) {
const chunk = await bundleWorkerEntry(config, id)
const encodedJs = `const encodedJs = "${Buffer.from(
chunk.code,
).toString('base64')}";`

const code =
// Using blob URL for SharedWorker results in multiple instances of a same worker
workerConstructor === 'Worker'
? `${encodedJs}
const decodeBase64 = (base64) => Uint8Array.from(atob(base64), c => c.charCodeAt(0));
const blob = typeof self !== "undefined" && self.Blob && new Blob([${
workerType === 'classic'
Expand Down Expand Up @@ -345,7 +359,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
: ''
}
}`
: `${encodedJs}
: `${encodedJs}
export default function WorkerWrapper(options) {
return new ${workerConstructor}(
"data:text/javascript;base64," + encodedJs,
Expand All @@ -354,36 +368,37 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
}
`

return {
code,
// Empty sourcemap to suppress Rollup warning
map: { mappings: '' },
return {
code,
// Empty sourcemap to suppress Rollup warning
map: { mappings: '' },
}
} else {
urlCode = JSON.stringify(await workerFileToUrl(config, id))
}
} else {
urlCode = JSON.stringify(await workerFileToUrl(config, id))
let url = await fileToUrl(cleanUrl(id), config, this)
url = injectQuery(url, `${WORKER_FILE_ID}&type=${workerType}`)
urlCode = JSON.stringify(url)
}
} else {
let url = await fileToUrl(cleanUrl(id), config, this)
url = injectQuery(url, `${WORKER_FILE_ID}&type=${workerType}`)
urlCode = JSON.stringify(url)
}

if (urlRE.test(id)) {
return {
code: `export default ${urlCode}`,
map: { mappings: '' }, // Empty sourcemap to suppress Rollup warning
if (urlRE.test(id)) {
return {
code: `export default ${urlCode}`,
map: { mappings: '' }, // Empty sourcemap to suppress Rollup warning
}
}
}

return {
code: `export default function WorkerWrapper(options) {
return {
code: `export default function WorkerWrapper(options) {
return new ${workerConstructor}(
${urlCode},
${workerTypeOption}
);
}`,
map: { mappings: '' }, // Empty sourcemap to suppress Rollup warning
}
map: { mappings: '' }, // Empty sourcemap to suppress Rollup warning
}
},
},

renderChunk(code, chunk, outputOptions) {
Expand Down