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

Allow importing classic Web Workers (not Module Workers) #2550

Closed
ebeloded opened this issue Mar 16, 2021 · 11 comments
Closed

Allow importing classic Web Workers (not Module Workers) #2550

ebeloded opened this issue Mar 16, 2021 · 11 comments
Labels
enhancement New feature or request feat: web workers p2-nice-to-have Not breaking anything but nice to have (priority)

Comments

@ebeloded
Copy link

Vite provides an easy way to import Web Workers, as described in the docs:

import MyWorker from './worker?worker'

const worker = new MyWorker()

This gets compiled into module worker initialization:

new Worker("/assets/index.ef0da162.js",{type:"module"})

Module workers are different from classic workers, and their support is very limited (Chrome 80+, no Safari, no Firefox), which means that shipping this code is not an option.

What's more, module workers are not drop-in replacement for classic workers, as they don't support importScripts, which existing implementations rely on to import scripts from CDN:

importScripts('https://unpkg.com/comlink/dist/umd/comlink.js')
importScripts('https://www.gstatic.com/firebasejs/8.3.0/firebase-app.js')

I would like to request support for classic workers by allowing to pass worker options to the worker constructor:

import MyWorker from './worker?worker'

const worker = new MyWorker() // classic worker
const worker = new MyWorker({type: 'classic'}) // classic worker
const worker = new MyWorker({type: 'module'}) // module worker

Worker constructor options should allow all of worker options, which look like this:

interface WorkerOptions {
    credentials?: RequestCredentials;
    name?: string;
    type?: WorkerType;
}

--

The alternative right now is to bundle worker code in a separate process and use the usual worker initialization:

const classicWorker = new Worker('./worker-built-separately.js')

This complicates the process and doesn't provide out of the box file name hashing. To add file name hashing would require further complications.

@ebeloded ebeloded changed the title Allow importing normal Web Workers (not Module Workers) Allow importing classic Web Workers (not Module Workers) Mar 16, 2021
@yyx990803 yyx990803 added the enhancement New feature or request label Mar 17, 2021
@userquin
Copy link
Contributor

userquin commented Mar 18, 2021

Is there a way to build with vite the worker with all its dependencies inside (no code splitting)?

I'm testing web workers and I can use { type: 'module'} (via esno + tsup with esm or cjs formats) on Safari and Firefox, the only restriction is to not have imports inside the worker (just plan javascript, es2017 or whatever we want) and building the worker with all dependencies inside requires to configure its dependencies as dev dependencies (in my case Comlink and dexiejs).

For example, I get my logic and put on new project, then I configure scripts directory with:
scripts/build.ts

import { execSync } from 'child_process'
// @ts-ignore
import { commands } from './commands'

for (const command of commands)
  execSync(command, { stdio: 'inherit' })

where commands.ts is:

export const commands = [
  'npx tsup src/parse-file-worker.ts --no-splitting --sourcemap --format cjs -d dist/build',
  'npx tsup src/parse-file-worker-module.ts  --sourcemap --target esnext --format esm -d dist/build',
]

then I add a new script to my package.json:

"scripts": {
  "build": "esno scripts/build.ts"
},

My worker looks like this parse-file-worker-module.ts:

import { expose } from 'comlink/dist/esm/comlink' // <== for esm format, for cjs format just use `import { expose } from 'comlink'`
import type { ParseFileFn } from '~/types'
import { parseFileAsync } from '~/parse-file-async' // <== 128 module dependencies

const parseFile: ParseFileFn = async(
  file,
  onFileInfo,
  onControlParsed,
  onProgress,
  onProcessError,
  onFinished,
  canceled,
) => {
  await parseFileAsync(
    file,
    onFileInfo,
    onControlParsed,
    onProgress,
    onProcessError,
    onFinished,
    canceled,
  )
}
// SSR compatibility
typeof self !== 'undefined' && expose(parseFile)

and finally I can use it (just setting the output on public directory) and referencing it via:

const worker = new Worker('/parse-file-worker-module.mjs', { type: 'module' })
const parseFile = wrap<ParseFileFn>(worker)

@userquin
Copy link
Contributor

userquin commented Mar 18, 2021

I forgot to mention that we can use ?worker with code splitting via dynamic imports with this (this only will work on chromiun based browsers, tested on Chrome and Edge):

async function useWebWorker() {
  const MyWorkerModule = await import('./my-worker?worker') // <=== code splitting
  return MyWorkerModule.default() // <=== use `default` function
}

Right now, the docs explains how to use with import but no with dynamic imports (the equivalent to previous code is):

import MyWorker from './my-worker?worker'

function useWebWorker() {
  return new MyWorker()
}

This should be included in the web workers section on vite docs: The default export will be a custom worker constructor is not 100% true, only when import is used, it is a javascript hack (typescript definition), with dynamic imports, we need to use what src/node/plugins/worker.ts module exports.

@ebeloded
Copy link
Author

ebeloded commented Jun 30, 2021

@Shinigami92 this issue doesn't have a PR anymore

@vigneshpa

This comment was marked as spam.

@shellwhale

This comment was marked as spam.

@poyoho
Copy link
Member

poyoho commented Mar 26, 2022

I think it can now be worked using new Worker(new URL(xxx, import.meta.url), {type: 'classic'}) syntax

@patak-dev
Copy link
Member

@ebeloded please open a new issue against latest if this doesn't cover your use case.

@poyoho we can also achieve the same without passing the options parameter, no?

new Worker(new URL(xxx, import.meta.url))

@poyoho
Copy link
Member

poyoho commented Mar 26, 2022

yes, I add the options to show the classic word. hhh

@ebeloded
Copy link
Author

ebeloded commented Mar 26, 2022

Is this a new but undocumented feature? A PR to have this implemented is still open.

@poyoho
Copy link
Member

poyoho commented Mar 27, 2022

The document has been changed. Maybe it hasn't been released yet. 2.9 hasn't been released yet. Now you can try 2.9.0-beta nine

@ebeloded
Copy link
Author

This is awesome @poyoho! Thanks for clarifying.

@github-actions github-actions bot locked and limited conversation to collaborators Apr 11, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request feat: web workers p2-nice-to-have Not breaking anything but nice to have (priority)
Projects
None yet
9 participants