Context:
I am loading several web workers in a dynamic way by using blob-based URIs. In my problem, this strategy is required because workers' content is partially generated using a template-based solution. See (1) in code
Problem:
Web workers include import
sentences with relative paths. As they are loaded using the Worker
constructor and it does not allow to specify a base path, relative imports cannot be properly resolved as absolute references. As a result, the worker loading process fails with no console message (I guess a 404 error is under the hood).
Example:
In this example we have created the following set of resources:
index.html
/scripts
/helpers
helper.x.js
helper.y.js
/loader
loader.js
/workers
worker.x.js
We have created a dynamic loader in loader.js:
function Loader () {
function asCode (text) { ... }
async function load (path) {
let response = await fetch (path)
let text = await response.text ()
let code = asCode (text) // (1)
let blob = new Blob ([code], { type: 'text/javascript' })
let url = URL.createObjectURL(blob)
let worker = new Worker (url, { type: 'module' })
return worker
}
return { load }
}
export default Loader ()
The worker in worker.x.js imports two naive helpers X & Y by using relative paths. Please, notice that if the path in import sentences where absolute (http://...) the problem described here would not appear:
import X from '../helpers/helper.x.js'
import Y from '../helpers/helper.y.js'
X.fx ()
Y.fy ()
Finally, in index.html we load the worker:
<body>
<script type="module">
import Loader from './scripts/loader/loader.js'
let WX = Loader.load ('./scripts/workers/worker.x.js')
</script>
</body>
Please, visit this replit to explore the project.
Question:
Is there any way to load web worker scripts in a dynamic way specifying the base path that must be used to resolve relative paths in inner import sentences? For me, an extension of the Worker constructor with a new base
parameter like this would be perfect :)
let WX = new Worker (path, { type: 'module', base: 'the-base-path' })
You could use your asCode
method to also inject the base URI.
In your template, insert a marker in front of your relative URLs that you'll replace at the time of building your blob.
To get the "fake" base URL as an absolute one, you can check the Response
object you get from fetch()
and extract the directory path from its .url
property.
Now, your "dynamic" module imports absolute paths and everyone is happy:
loader.js
function Loader () {
function asCode (text, baseURI) {
return baseURI
? text.replace(/\$base_url\$/g, baseURI)
: text;
}
async function load (path) {
let response = await fetch (path)
let text = await response.text ()
let absolute = response.url;
// remove the worker script's file name to get its base URI
let href = absolute.substring(0, absolute.lastIndexOf('/'));
let code = asCode (text, href)
let blob = new Blob ([code], {type: 'text/javascript' })
let url = URL.createObjectURL(blob)
let worker = new Worker (url, { type: 'module' })
return worker
}
return { load }
}
export default Loader ()
And worker.x.js now looks like
import X from '$base_url$/../helpers/helper.x.js'
import Y from '$base_url$/../helpers/helper.y.js'
X.fx ()
Y.fy ()