149 lines
4.1 KiB
JavaScript
149 lines
4.1 KiB
JavaScript
/**
|
|
* Main content for the worker that handles the loading and execution of
|
|
* modules within it.
|
|
*/
|
|
export function workerBootstrap() {
|
|
const modules = Object.create(null)
|
|
|
|
// Handle messages for registering a module
|
|
function registerModule({id, name, dependencies=[], init=function(){}, getTransferables=null}, callback) {
|
|
// Only register once
|
|
if (modules[id]) return
|
|
|
|
try {
|
|
// If any dependencies are modules, ensure they're registered and grab their value
|
|
dependencies = dependencies.map(dep => {
|
|
if (dep && dep.isWorkerModule) {
|
|
registerModule(dep, depResult => {
|
|
if (depResult instanceof Error) throw depResult
|
|
})
|
|
dep = modules[dep.id].value
|
|
}
|
|
return dep
|
|
})
|
|
|
|
// Rehydrate functions
|
|
init = rehydrate(`<${name}>.init`, init)
|
|
if (getTransferables) {
|
|
getTransferables = rehydrate(`<${name}>.getTransferables`, getTransferables)
|
|
}
|
|
|
|
// Initialize the module and store its value
|
|
let value = null
|
|
if (typeof init === 'function') {
|
|
value = init(...dependencies)
|
|
} else {
|
|
console.error('worker module init function failed to rehydrate')
|
|
}
|
|
modules[id] = {
|
|
id,
|
|
value,
|
|
getTransferables
|
|
}
|
|
callback(value)
|
|
} catch(err) {
|
|
if (!(err && err.noLog)) {
|
|
console.error(err)
|
|
}
|
|
callback(err)
|
|
}
|
|
}
|
|
|
|
// Handle messages for calling a registered module's result function
|
|
function callModule({id, args}, callback) {
|
|
if (!modules[id] || typeof modules[id].value !== 'function') {
|
|
callback(new Error(`Worker module ${id}: not found or its 'init' did not return a function`))
|
|
}
|
|
try {
|
|
const result = modules[id].value(...args)
|
|
if (result && typeof result.then === 'function') {
|
|
result.then(handleResult, rej => callback(rej instanceof Error ? rej : new Error('' + rej)))
|
|
} else {
|
|
handleResult(result)
|
|
}
|
|
} catch(err) {
|
|
callback(err)
|
|
}
|
|
function handleResult(result) {
|
|
try {
|
|
let tx = modules[id].getTransferables && modules[id].getTransferables(result)
|
|
if (!tx || !Array.isArray(tx) || !tx.length) {
|
|
tx = undefined //postMessage is very picky about not passing null or empty transferables
|
|
}
|
|
callback(result, tx)
|
|
} catch(err) {
|
|
console.error(err)
|
|
callback(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
function rehydrate(name, str) {
|
|
let result = void 0
|
|
self.troikaDefine = r => result = r
|
|
let url = URL.createObjectURL(
|
|
new Blob(
|
|
[`/** ${name.replace(/\*/g, '')} **/\n\ntroikaDefine(\n${str}\n)`],
|
|
{type: 'application/javascript'}
|
|
)
|
|
)
|
|
try {
|
|
importScripts(url)
|
|
} catch(err) {
|
|
console.error(err)
|
|
}
|
|
URL.revokeObjectURL(url)
|
|
delete self.troikaDefine
|
|
return result
|
|
}
|
|
|
|
// Handler for all messages within the worker
|
|
self.addEventListener('message', e => {
|
|
const {messageId, action, data} = e.data
|
|
try {
|
|
// Module registration
|
|
if (action === 'registerModule') {
|
|
registerModule(data, result => {
|
|
if (result instanceof Error) {
|
|
postMessage({
|
|
messageId,
|
|
success: false,
|
|
error: result.message
|
|
})
|
|
} else {
|
|
postMessage({
|
|
messageId,
|
|
success: true,
|
|
result: {isCallable: typeof result === 'function'}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
// Invocation
|
|
if (action === 'callModule') {
|
|
callModule(data, (result, transferables) => {
|
|
if (result instanceof Error) {
|
|
postMessage({
|
|
messageId,
|
|
success: false,
|
|
error: result.message
|
|
})
|
|
} else {
|
|
postMessage({
|
|
messageId,
|
|
success: true,
|
|
result
|
|
}, transferables || undefined)
|
|
}
|
|
})
|
|
}
|
|
} catch(err) {
|
|
postMessage({
|
|
messageId,
|
|
success: false,
|
|
error: err.stack
|
|
})
|
|
}
|
|
})
|
|
}
|