-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4a9713a
commit c181b04
Showing
7 changed files
with
297 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
'use strict'; | ||
|
||
module.exports = { | ||
// Message types | ||
INCOMING: 1, | ||
SUCCESS: 2, | ||
FAIL: 3, | ||
NEXT: 4, | ||
// Hook types | ||
INIT: 1, | ||
RESOLVE: 2, | ||
LOAD: 3, | ||
// Error types | ||
ERROR: 1, | ||
OTHER: 2 | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
'use strict'; | ||
|
||
// Modules | ||
const SynchronousWorker = require('@matteo.collina/worker'); | ||
|
||
// Imports | ||
const {createChannels, wrapError} = require('./utils.js'), | ||
{RESOLVE, SUCCESS, FAIL, INIT, NEXT} = require('./constants.js'); | ||
|
||
// Exports | ||
|
||
module.exports = function preload(loaderUrl, port, getBuiltin) { | ||
const {listen, message, post} = createChannels(port, 'inner'); | ||
port.unref(); | ||
|
||
let loader; | ||
try { | ||
loader = importSync(loaderUrl); | ||
} catch (err) { | ||
post(0, FAIL, INIT, wrapError(err)); | ||
return; | ||
} | ||
|
||
const {resolve, load} = loader; | ||
|
||
listen((channelId, hook, data) => (hook === RESOLVE ? resolve : load)( | ||
...data, | ||
(specifier, context) => message(channelId, NEXT, hook, [specifier, context]) | ||
)); | ||
|
||
post(0, SUCCESS, INIT, {resolve: !!loader.resolve, load: !!loader.load}); | ||
|
||
if (loader.globalPreload) { | ||
// TODO Proxy outer port | ||
const preloadCode = loader.globalPreload(); | ||
|
||
// TODO Proxy inner port | ||
// eslint-disable-next-line no-new-func | ||
new Function('getBuiltin', preloadCode)(getBuiltin); | ||
} | ||
}; | ||
|
||
function importSync(url) { | ||
const worker = new SynchronousWorker(); | ||
const workerImport = worker.createRequire(__filename)('./import.js'); | ||
const promise = workerImport(url); | ||
return worker.runLoopUntilPromiseResolved(promise); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
'use strict'; | ||
|
||
// Modules | ||
const {isFunction} = require('is-it-type'); | ||
|
||
// Imports | ||
const {INCOMING, SUCCESS, FAIL, ERROR, OTHER} = require('./constants.js'); | ||
|
||
// Exports | ||
|
||
module.exports = {createChannels, defer, wrapError, unwrapError}; | ||
|
||
function createChannels(port, logName) { | ||
let nextChannelId = 1; | ||
const callbacks = new Map(); | ||
|
||
function listen(onIncoming) { | ||
port.onmessage = ({data: {id, type, hook, data}}) => { | ||
// TODO Remove console.log | ||
// eslint-disable-next-line no-console | ||
console.log(`${logName} received message:`, {id, type, hook, data}); | ||
|
||
if (type === INCOMING) { | ||
onIncoming(id, hook, data).then( | ||
res => post(id, SUCCESS, hook, res), | ||
err => post(id, FAIL, hook, wrapError(err)) | ||
); | ||
return; | ||
} | ||
callbacks.get(id)(type, data); | ||
}; | ||
} | ||
|
||
function addCallback(messageId, callback) { | ||
callbacks.set(messageId, (type, data) => { | ||
const shouldDelete = callback(type, data); | ||
if (shouldDelete) callbacks.delete(messageId); | ||
}); | ||
} | ||
|
||
function createChannel(hook, data, onMessage) { | ||
const channelId = nextChannelId++; | ||
return message(channelId, INCOMING, hook, data, onMessage); | ||
} | ||
|
||
function message(channelId, type, hook, data, onMessage) { | ||
const deferred = defer(); | ||
addCallback(channelId, (replyType, replyData) => { | ||
if (replyType === SUCCESS) { | ||
deferred.resolve(replyData); | ||
return true; | ||
} | ||
|
||
if (replyType === FAIL) { | ||
deferred.reject(unwrapError(replyData)); | ||
return true; | ||
} | ||
|
||
onMessage(replyType, replyData).then( | ||
value => post(channelId, SUCCESS, hook, value), | ||
err => post(channelId, FAIL, hook, wrapError(err)) | ||
); | ||
return false; | ||
}); | ||
|
||
post(channelId, type, hook, data); | ||
|
||
return deferred.promise; | ||
} | ||
|
||
function post(id, type, hook, data) { | ||
// TODO Remove console.log | ||
// eslint-disable-next-line no-console | ||
console.log(`${logName} sending message:`, {id, type, hook, data}); | ||
port.postMessage({id, type, hook, data}); | ||
} | ||
|
||
return {listen, addCallback, createChannel, message, post}; | ||
} | ||
|
||
function defer() { | ||
const deferred = {promise: null, resolve: null, reject: null}; | ||
deferred.promise = new Promise((resolve, reject) => { | ||
deferred.resolve = resolve; | ||
deferred.reject = reject; | ||
}); | ||
return deferred; | ||
} | ||
|
||
function wrapError(err) { | ||
if (isFunction(err)) return {type: ERROR, message: 'Unserializable error', stack: null, props: null}; | ||
|
||
if (err instanceof Error) { | ||
const {message, stack, ...props} = err; | ||
return {type: ERROR, message, stack, props}; | ||
} | ||
|
||
return {type: OTHER, value: err}; | ||
} | ||
|
||
function unwrapError(errDef) { | ||
if (errDef.type !== ERROR) return errDef.value; | ||
|
||
const err = new Error(errDef.message); | ||
if (errDef.stack) err.stack = errDef.stack; | ||
Object.assign(err, errDef.props); | ||
return err; | ||
} |
Oops, something went wrong.