// solc-js-webworker.js
const solcjs = require('solc-js')
var lid = 0
var compiler
onmessage = async event => {
const [lid, from, path, ref, type, body] = event.data
if (type === 'version') retrun version(from, lid, body)
if (type === 'compile') return compile(body)
// ...
}
function version (from, path, ref, body) {
compiler = await solcjs(version)
self.postMessage([lid++, from, path, ref, compiler.version])
}
function compile (from, path, ref, body) {
if (!compiler) compiler = solcjs()
const output = await solcjs(body)
self.postMessage([lid++, from, path, ref, output])
// TODO: 1. handle errors
// TODO: 2. enable multiple round trips to support resolving imports
}
// solc-js-worker-proxy.js
const worker_solcjs = new Worker('bundle_solc-js-webworker.js')
worker_solcjs.onmessage = handler
const from = '/', sent = [], received = {}
var lid = 1
module.exports = solcjs_worker_proxy
async function solcjs_worker_proxy (version) {
return new Promise((resolve, reject) => {
const message = [lid++, from, /*path*/'/', /*ref*/0/*(initialize new conversation)*/, /*type*/'init'/*(create new compiler)*/, /*body*/version || 'latest']
sent.push([message, [resolve, reject])
worker_solcjs.postMessage(message)
})
}
function handler ({ data: message }) {
const [lid_, from_, path_, ref_, type_, body_] = message
try {
const [[_lid, _from, _path, _ref, _type, _body], executor ] = msglog[ref_]
if ((ref_ !== _lid) || (path_ !== _from)) throw new Error('@TODO: error in received `data`')
;(received[from_] || (received[from_] = [])).push(message)
if (_type === 'init') return init([type_, body_], executor)
if (_type === 'compile') return compile([type_, body_], executor)
if (_type === 'help') return _help([type_, body_])
// @TODO: handle more type's
} catch (e) {
console.error(e) // @TODO: handle communication initiated by worker or unexpected message from worker
}
}
function init ([type, body], [resolve, reject]) {
if (type === 'error') return reject(body)
if (type === 'ok') {
const path = `/compiler/${body}` // e.g. "/compiler/1"
async function compile (sourcecode) {
return new Promise((resolve, reject) => {
const message = [lid++, from, path, 0, 'compile', sourcecode]
sent.push([message, [resolve, reject])
worker_solcjs.postMessage(message)
})
}
return resolve(compile)
}
reject(`@TODO: handle unknown response type '${type}'`)
}
function compile (type, body, [resolve, reject]) {
if (type === 'error') return reject(body)
if (type === 'ok') return resolve(body)
reject(`@TODO: handle unknown response type '${type}'`)
}
function _help ([type, body]) {
if (type === 'ok') return console.log(body)
// e.g. { '/': { types: ['help', 'init'] } }
// or e.g. { '/': { types: ['help', 'init'] }, '/compiler/1': { types: ['compile', 'versions', 'version2url'] } }
if (type === 'error') {}
throw new Error(`@TODO: handle unknown response type '${type}'`)
}
Usage:
const solcjs = require('solc-js-worker-proxy')
const compiler = solcjs()
const output = solcjs`contract Foo {}`
// index.js
// package.json#scripts: { "build": "browserify index.js > bundle.js" }
// npm run build
const worker = require('./worker')
const renderer = require('./renderer')
if (location.pathname === '/' || location.pathname === '/index.html') renderer()
else if (location.pathname === '/bundle.js') worker() // that's actually how it works in web workers :-)
else console.error('unexpected pathname', location.pathname)
// renderer.js
module.exports = () => {
const worker = new Worker(document.currentScript.src) // in practice a "bundle" to inline all required modules
worker.onmessage = event => console.log('[from worker to renderer] ', event.data)
worker.postMessage('hello worker')
// ...
}
// worker.js
module.exports = () => {
onmessage = async event => {
console.log('[from renderer to worker] ', event.data)
}
postMessage('hello renderer')
// ...
}