-
Notifications
You must be signed in to change notification settings - Fork 47
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
Showing
21 changed files
with
17,472 additions
and
0 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
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,3 @@ | ||
{ | ||
"presets": ["diffhtml-imports"] | ||
} |
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 @@ | ||
node_modules |
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,81 @@ | ||
# <±/> diffHTML Worker Middleware | ||
|
||
Stable Version: 1.0.0-beta.23 | ||
|
||
Used to separate the diffing and patching stages of rendering across threads. | ||
The main thread receives payloads containing mutations to apply, all UI logic | ||
is isolated within a worker thread. | ||
|
||
Can be used with Node.js servers and pass patches to the client using Web | ||
Sockets. | ||
|
||
## Installation | ||
|
||
``` sh | ||
npm install diffhtml-middleware-worker | ||
``` | ||
|
||
## Usage | ||
|
||
When you create a worker, you bind it to a mount point and all `innerHTML` or | ||
`outerHTML` calls that get made in the worker are intercepted and passed to the | ||
main thread. | ||
|
||
### In a browser | ||
|
||
To create a web worker in the browser, import the `createWebWorker` function. | ||
Invoke this with a file path pointing to the location of your module that | ||
contains the rendering logic. This file path will be explained more after the | ||
following code snippet. | ||
|
||
You must import either the core or lite version in the main thread to do the | ||
actual patching. The lite version is preferable due to the smaller filesize. | ||
Then register the middleware into the main thread. | ||
|
||
```js | ||
import { use } from 'https://diffhtml.org/core/lite'; | ||
import { mainTask, createWebWorker } from 'https://diffhtml.org/middleware-worker'; | ||
|
||
use(mainTask()); | ||
``` | ||
|
||
Once the middleware is registered, you can create web workers using the helper | ||
function. If you already have a worker you can pass it instead of a string to | ||
have the events hooked up. | ||
|
||
```js | ||
const mount = document.body; | ||
createWebWorker('./path/to/worker.js', { mount }); | ||
``` | ||
|
||
The above code will create a worker that exists at `worker.js` and proxy all | ||
rendering from it into the mount point, in this case the `<body>` tag. You | ||
must register the `workerTask` middleware inside the worker to ensure patches | ||
are properly passed to the main thread. | ||
|
||
```js | ||
import { use, html, createTree, innerHTML } from 'https://diffhtml.org/core'; | ||
import { workerTask } from 'https://diffhtml.org/middleware-worker'; | ||
|
||
use(workerTask()); | ||
``` | ||
|
||
In the worker you must create a placeholder to render into. This will | ||
emulate the mount in the main thread. | ||
|
||
```js | ||
// Create an empty fragment to render into, this represents the body tag | ||
// in the main thread. | ||
const placeholder = createTree(); | ||
|
||
// Any outerHTML or innerHTML calls will be proxied to the main thread mount | ||
// point. | ||
innerHTML(placeholder, html` | ||
<h1>Hello world from a worker thread!</h1> | ||
`); | ||
``` | ||
|
||
### With Node.js | ||
|
||
|
||
|
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 @@ | ||
export * from './dist/es/index.js'; |
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,14 @@ | ||
{ | ||
"compilerOptions": { | ||
"strict": true, | ||
"module": "esnext", | ||
"target": "es2017", | ||
"declaration": true, | ||
"checkJs": true, | ||
"noUnusedParameters": true, | ||
"noUnusedLocals": true, | ||
"outDir": "./dist/typings", | ||
"lib": ["es2017", "dom"] | ||
}, | ||
"include": ["lib/**/*"] | ||
} |
60 changes: 60 additions & 0 deletions
60
packages/diffhtml-middleware-worker/lib/create-node-worker.js
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,60 @@ | ||
import { type } from './util/types'; | ||
|
||
let Worker = null; | ||
|
||
if (typeof global !== 'undefined') { | ||
Worker = (await import('worker_threads')).Worker; | ||
} | ||
|
||
const { stringify } = JSON; | ||
|
||
/** | ||
* | ||
* @param {string} workerPath | ||
* @param {Object} options | ||
* @param {any} options.socket | ||
* @param {Object} options.workerOpts | ||
* @returns {Worker} | ||
*/ | ||
export const createNodeWorker = (workerPath, { socket, workerOpts }) => { | ||
const worker = new Worker(workerPath, { ...(workerOpts || {}) }); | ||
|
||
/** | ||
* @type {string} msg | ||
*/ | ||
const onSocketMessage = (msg) => { | ||
worker.postMessage(msg); | ||
}; | ||
|
||
worker.on('message', data => { | ||
socket.send(stringify(data)); | ||
}) | ||
.on('error', (error) => { | ||
console.error(error); | ||
|
||
socket.send(stringify({ | ||
type: 'log', | ||
level: 'error', | ||
message: String(error.stack), | ||
})); | ||
|
||
socket.send(stringify({ | ||
type: 'clear', | ||
})); | ||
|
||
return true; | ||
}) | ||
.on('exit', () => { | ||
socket.off('message', onSocketMessage); | ||
|
||
setTimeout(() => { | ||
createNodeWorker(workerPath, { socket, workerOpts }); | ||
}, 2000); | ||
}); | ||
|
||
// Handle incoming messages, must be on main thread to support synchronous | ||
// worker calls. | ||
socket.on('message', onSocketMessage); | ||
|
||
return worker; | ||
}; |
30 changes: 30 additions & 0 deletions
30
packages/diffhtml-middleware-worker/lib/create-web-socket.js
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,30 @@ | ||
import diff from './util/binding'; | ||
|
||
const { innerHTML, html } = diff; | ||
const { parse } = JSON; | ||
|
||
export const createWebSocket = (socketUrl, { mount, socketOptions }) => { | ||
const socket = new WebSocket(socketUrl, socketOptions); | ||
|
||
socket.addEventListener('message', async e => { | ||
const { type, ...rest } = parse(e.data); | ||
|
||
// TODO Deprecate the 'clear' event. This is currently used in the Node | ||
// worker when an error is encountered. This cleans up the markup to avoid | ||
// issues during rendering. | ||
if (type === 'clear') { | ||
mount.innerHTML = ''; | ||
} | ||
|
||
if (type === 'patches') { | ||
innerHTML(mount, null, { patches: rest.patches }); | ||
} | ||
|
||
if (type === 'log') { | ||
const { level, message } = rest; | ||
console[level](...[].concat(message)); | ||
} | ||
}); | ||
|
||
return socket; | ||
}; |
37 changes: 37 additions & 0 deletions
37
packages/diffhtml-middleware-worker/lib/create-web-worker.js
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,37 @@ | ||
import diff from './util/binding'; | ||
import { type, Mount } from './util/types'; | ||
|
||
const { createTree, innerHTML } = diff; | ||
const { stringify } = JSON; | ||
|
||
/** | ||
* Provides a Web Worker implementation | ||
* | ||
* @param {string} path - Location of worker script | ||
* @param {Object} options | ||
* @param {Mount} options.mount - DOM Node or VTree to render worker contents into | ||
* @param {Object} workerOpts - Supply additional options to new Worker() | ||
* @returns {Worker} | ||
*/ | ||
export const createWebWorker = (path, { mount, workerOpts }) => { | ||
const worker = new Worker(path, { type: 'module', ...(workerOpts || {}) }); | ||
|
||
worker.onmessage = e => { | ||
const { type, ...rest } = e.data; | ||
|
||
if (type === 'patches') { | ||
/** @type {any} */ | ||
const transactionConfigAsAny = ({ patches: rest.patches }); | ||
innerHTML(mount, null, transactionConfigAsAny); | ||
} | ||
}; | ||
|
||
worker.onerror = e => { | ||
innerHTML(mount, createTree('h1', [ | ||
createTree('#text', `Error in worker: ${path}`), | ||
createTree('pre', stringify(e, null, 2)), | ||
])); | ||
}; | ||
|
||
return worker; | ||
}; |
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,17 @@ | ||
import globalThis from './util/global'; | ||
import nodeBuffer from './util/node-buffer'; | ||
|
||
let Blob = globalThis.Blob; | ||
|
||
// First attempt to load using Node.js | ||
if (typeof Blob === 'undefined' && nodeBuffer) { | ||
Blob = nodeBuffer.Blob; | ||
} | ||
|
||
// If still not available, throw an error. | ||
if (typeof Blob === 'undefined') { | ||
throw new Error('Missing required Blob dependency'); | ||
} | ||
|
||
// Extract UUID from object URL generated for an arbitrary blob. | ||
export const getUUID = () => URL.createObjectURL(new Blob()).slice(31); |
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,6 @@ | ||
export { getUUID } from './get-uuid'; | ||
export { createWebSocket } from './create-web-socket'; | ||
export { createWebWorker } from './create-web-worker'; | ||
export { createNodeWorker } from './create-node-worker'; | ||
export { mainTask } from './main-task'; | ||
export { workerTask } from './worker-task'; |
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,83 @@ | ||
import diff from './util/binding'; | ||
|
||
const { Internals } = diff; | ||
|
||
const { assign } = Object; | ||
const { stringify } = JSON; | ||
const linker = new Map(); | ||
|
||
export const mainTask = ({ | ||
// TODO Merge socket and worker args together into one named bridge | ||
// or something similar. | ||
// | ||
// WebSocket connection for the main thread. | ||
socket = null, | ||
worker = null, | ||
} = {}) => assign(function webWorkerTask(transaction) { | ||
const currentTasks = transaction.tasks; | ||
const indexOfSyncTrees = currentTasks.indexOf(Internals.tasks.syncTrees); | ||
|
||
// Only run this middleware when patches are present. | ||
if (!('patches' in transaction.config)) { | ||
return; | ||
} | ||
|
||
const link = vTree => { | ||
if (vTree && vTree.childNodes) { | ||
Internals.Pool.memory.protected.add(vTree); | ||
linker.set(vTree.__link, vTree); | ||
vTree.childNodes.forEach(x => link(x)); | ||
} | ||
}; | ||
|
||
// Replace syncTrees with injectPatches | ||
currentTasks.splice(indexOfSyncTrees, 1, function injectPatches() { | ||
transaction.patches = transaction.config.patches.map(x => { | ||
// Handle dom events, custom element properties, etc. Any time you need | ||
// a function, we proxy the call and the arguments. | ||
if (x && x.__caller) { | ||
return function(e) { | ||
// TODO handled by synthetic events middleware | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
|
||
if (socket) { | ||
socket.send(stringify({ type: 'event', ...x })); | ||
} | ||
else { | ||
worker.postMessage({ type: 'event', ...x }); | ||
} | ||
}; | ||
} | ||
|
||
if (!x || typeof x !== 'object' || !('__link' in x)) { | ||
return x; | ||
} | ||
|
||
let vTree = x; | ||
|
||
if (linker.has(x.__link)) { | ||
vTree = linker.get(x.__link); | ||
return vTree; | ||
} | ||
else if (x.__link === 'mount') { | ||
vTree = transaction.oldTree; | ||
} | ||
else { | ||
link(vTree); | ||
} | ||
|
||
if (((x && x.isSvg) || (vTree && vTree.isSvg)) && vTree) { | ||
transaction.state.svgElements.add(vTree); | ||
} | ||
|
||
return vTree; | ||
}); | ||
}); | ||
}, { | ||
releaseHook: vTree => { | ||
if (vTree && vTree.__link) { | ||
linker.delete(vTree.__link); | ||
} | ||
}, | ||
}); |
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,9 @@ | ||
import globalThis from './global'; | ||
import { DIFF_BINDING } from './types'; | ||
|
||
/** | ||
* @returns {DIFF_BINDING} | ||
*/ | ||
export default /** @type {DIFF_BINDING} */ ( | ||
/** @type {any} */ (globalThis)[Symbol.for('diffHTML')] | ||
); |
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,6 @@ | ||
/** @type {any} */ | ||
export default ( | ||
typeof global === 'object' | ||
? global | ||
: (typeof window === 'object' ? window : self) || {} | ||
); |
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,8 @@ | ||
let nodeBuffer = null; | ||
|
||
try { | ||
nodeBuffer = (await import('buffer')).default; | ||
} | ||
catch {} | ||
|
||
export default nodeBuffer; |
8 changes: 8 additions & 0 deletions
8
packages/diffhtml-middleware-worker/lib/util/node-worker-threads.js
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,8 @@ | ||
let nodeWorkerThreads = null; | ||
|
||
try { | ||
nodeWorkerThreads = (await import('worker_threads')).default; | ||
} | ||
catch {} | ||
|
||
export default nodeWorkerThreads; |
Oops, something went wrong.