Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 7 additions & 1 deletion packages/playground/worker/__tests__/es/es-worker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ if (isBuild) {
// assert correct files
test('inlined code generation', async () => {
const files = fs.readdirSync(assetsDir)
expect(files.length).toBe(21)
expect(files.length).toBe(23)
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'))
Expand Down Expand Up @@ -100,3 +100,9 @@ test('emit chunk', async () => {
'"A string/es/"'
)
})

test('url query worker', async () => {
expect(await page.textContent('.simple-worker-url')).toMatch(
'Hello from simple worker!'
)
})
8 changes: 7 additions & 1 deletion packages/playground/worker/__tests__/iife/worker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ if (isBuild) {
// assert correct files
test('inlined code generation', async () => {
const files = fs.readdirSync(assetsDir)
expect(files.length).toBe(12)
expect(files.length).toBe(14)
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'))
Expand Down Expand Up @@ -94,3 +94,9 @@ test('classic worker', async () => {
expect(await page.textContent('.classic-worker')).toMatch('A classic')
expect(await page.textContent('.classic-shared-worker')).toMatch('A classic')
})

test('url query worker', async () => {
expect(await page.textContent('.simple-worker-url')).toMatch(
'Hello from simple worker!'
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ if (isBuild) {
test('sourcemap generation for web workers', async () => {
const files = fs.readdirSync(assetsDir)
// should have 2 worker chunk
expect(files.length).toBe(24)
expect(files.length).toBe(28)
const index = files.find((f) => f.includes('main-module'))
const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
const indexSourcemap = getSourceMapUrl(content)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ if (isBuild) {
test('sourcemap generation for web workers', async () => {
const files = fs.readdirSync(assetsDir)
// should have 2 worker chunk
expect(files.length).toBe(12)
expect(files.length).toBe(14)
const index = files.find((f) => f.includes('main-module'))
const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
const indexSourcemap = getSourceMapUrl(content)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ if (isBuild) {
test('sourcemap generation for web workers', async () => {
const files = fs.readdirSync(assetsDir)
// should have 2 worker chunk
expect(files.length).toBe(24)
expect(files.length).toBe(28)
const index = files.find((f) => f.includes('main-module'))
const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
const indexSourcemap = getSourceMapUrl(content)
Expand Down
6 changes: 6 additions & 0 deletions packages/playground/worker/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ <h2 class="format-iife">format iife:</h2>
</p>
<code class="classic-shared-worker"></code>

<p>
new Worker(new URL('../simple-worker.js', import.meta.url).href)
<span class="classname">.simple-worker-url</span>
</p>
<code class="simple-worker-url"></code>

<hr />
<h2 class="format-es"></h2>

Expand Down
1 change: 1 addition & 0 deletions packages/playground/worker/simple-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
self.postMessage('Hello from simple worker!')
11 changes: 11 additions & 0 deletions packages/playground/worker/worker/main-url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import workerUrl from '../simple-worker?worker&url'

function text(el, text) {
document.querySelector(el).textContent = text
}

const worker = new Worker(workerUrl, { type: 'classic' })

worker.addEventListener('message', (ev) => {
text('.simple-worker-url', JSON.stringify(ev.data))
})
1 change: 1 addition & 0 deletions packages/playground/worker/worker/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* flag: will replace in vite config import("./format-es.js") */
import('./main-module')
import('./main-classic')
import('./main-url')
45 changes: 40 additions & 5 deletions packages/vite/src/node/plugins/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ 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 { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants'
import path from 'path'
import { onRollupWarning } from '../build'
import type { TransformPluginContext, EmittedFile } from 'rollup'
import type { ViteDevServer } from '../server'

interface WorkerCache {
// save worker bundle emitted files avoid overwrites the same file.
Expand All @@ -23,7 +24,9 @@ interface WorkerCache {
emitted: Map<string, string>
}

const WorkerFileId = 'worker_file'
export type WorkerType = 'classic' | 'module' | 'ignore'

export const WorkerFileId = 'worker_file'
const workerCache = new WeakMap<ResolvedConfig, WorkerCache>()

function emitWorkerFile(
Expand Down Expand Up @@ -195,10 +198,15 @@ export async function workerFileToUrl(
export function webWorkerPlugin(config: ResolvedConfig): Plugin {
const isBuild = config.command === 'build'
const isWorker = config.isWorker
let server: ViteDevServer

return {
name: 'vite:worker',

configureServer(_server) {
server = _server
},

buildStart() {
workerCache.set(config, {
assets: new Map(),
Expand All @@ -220,11 +228,30 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
}
},

async transform(_, id) {
async transform(raw, id) {
const query = parseRequest(id)
if (query && query[WorkerFileId] != null) {
if (query && query[WorkerFileId] != null && query['type'] != null) {
const workerType = query['type'] as WorkerType
let injectEnv = ''

if (workerType === 'classic') {
injectEnv = `importScripts('${ENV_PUBLIC_PATH}')\n`
} else if (workerType === 'module') {
injectEnv = `import '${ENV_PUBLIC_PATH}'\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 || ''
}
}

return {
code: `import '${ENV_PUBLIC_PATH}'\n` + _
code: injectEnv + raw
}
}
if (
Expand All @@ -233,6 +260,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
) {
return
}
const isUrl = query.url != null

let url: string
if (isBuild) {
Expand Down Expand Up @@ -264,6 +292,13 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
url = injectQuery(url, WorkerFileId)
}

if (isUrl) {
return {
code: `export default ${JSON.stringify(url)}`,
map: { mappings: '' } // Empty sourcemap to supress Rolup warning
}
}

const workerConstructor =
query.sharedworker != null ? 'SharedWorker' : 'Worker'
const workerOptions = { type: 'module' }
Expand Down
39 changes: 3 additions & 36 deletions packages/vite/src/node/plugins/workerImportMetaUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,15 @@ import type { Plugin } from '../plugin'
import { fileToUrl } from './asset'
import { cleanUrl, injectQuery } from '../utils'
import path from 'path'
import { workerFileToUrl } from './worker'
import type { WorkerType } from './worker'
import { workerFileToUrl, WorkerFileId } from './worker'
import { parseRequest } from '../utils'
import { ENV_ENTRY, ENV_PUBLIC_PATH } from '../constants'
import MagicString from 'magic-string'
import type { ViteDevServer } from '..'
import type { RollupError } from 'rollup'
import { emptyString } from '../cleanString'

type WorkerType = 'classic' | 'module' | 'ignore'
const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\//

const WORKER_FILE_ID = 'worker_url_file'

function getWorkerType(raw: string, clean: string, i: number): WorkerType {
function err(e: string, pos: number) {
const error = new Error(e) as RollupError
Expand Down Expand Up @@ -69,41 +65,12 @@ function getWorkerType(raw: string, clean: string, i: number): WorkerType {

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

return {
name: 'vite:worker-import-meta-url',

configureServer(_server) {
server = _server
},

async transform(code, id, options) {
const query = parseRequest(id)
if (query && query[WORKER_FILE_ID] != null && query['type'] != null) {
const workerType = query['type'] as WorkerType
let injectEnv = ''

if (workerType === 'classic') {
injectEnv = `importScripts('${ENV_PUBLIC_PATH}')\n`
} else if (workerType === 'module') {
injectEnv = `import '${ENV_PUBLIC_PATH}'\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 || ''
}
}

return {
code: injectEnv + code
}
}
let s: MagicString | undefined
if (
(code.includes('new Worker') || code.includes('new SharedWorker')) &&
Expand Down Expand Up @@ -150,7 +117,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
url = await workerFileToUrl(this, config, file, query)
} else {
url = await fileToUrl(cleanUrl(file), config, this)
url = injectQuery(url, WORKER_FILE_ID)
url = injectQuery(url, WorkerFileId)
url = injectQuery(url, `type=${workerType}`)
}
s.overwrite(urlIndex, urlIndex + exp.length, JSON.stringify(url), {
Expand Down