Move terser off of the main thread

This commit is contained in:
Jade Ellis 2024-05-13 19:25:51 +01:00
parent 844164bda9
commit f23e0417ed
7 changed files with 206 additions and 4 deletions

View file

@ -19,6 +19,7 @@
"@sveltejs/kit": "^2.5.7",
"@sveltejs/vite-plugin-svelte": "^3.1.0",
"@types/node": "^20.12.7",
"@types/sharedworker": "^0.0.115",
"glob": "^10.3.12",
"mdsvex": "^0.11.0",
"rehype-slug": "^6.0.0",

View file

@ -0,0 +1,48 @@
import type { MinifyOptions, MinifyOutput } from "terser";
import { recieveMessageData, sendMessageData } from "./util";
export function init() {
let worker: SharedWorker;
let currentId = 0;
let terserModule: typeof import("terser");
let promises: { [id: number]: [(value: MinifyOutput | PromiseLike<MinifyOutput>) => void, (reason?: any) => void] } = {};
return {
minify: async function minify(files: string | string[] | {
[file: string]: string;
}, options?: MinifyOptions): Promise<MinifyOutput> {
if (!!window.SharedWorker) {
if (!worker) {
worker = new SharedWorker(new URL('./terserWorker.ts', import.meta.url), { type: "module" })
worker.port.onmessage = (e: MessageEvent<any>) => {
// invoke the promise's resolve() or reject() depending on whether there was an error.
promises[e.data[recieveMessageData.MessageId]][e.data[recieveMessageData.MessageType]](e.data[recieveMessageData.Return]);
// ... then delete the promise controller
delete promises[e.data[recieveMessageData.MessageId]];
}
}
worker.port.start()
return new Promise((resolve, reject) => {
promises[++currentId] = [resolve, reject];
let data = {
[sendMessageData.MessageId]: currentId,
[sendMessageData.Parameters]: [files, options
]
}
worker.port.postMessage(data)
});
} else {
if (!terserModule) {
terserModule = await import("terser")
}
return await terserModule.minify(files, options)
}
}
}
}

View file

@ -0,0 +1,35 @@
import { minify, type MinifyOptions } from "terser";
import { recieveMessageTypes, sendMessageData } from "./util";
/// <reference lib="sharedworker " />
declare var self: SharedWorkerGlobalScope;
self.onconnect = function (event) {
const port = event.ports[0];
port.onmessage = function (e: MessageEvent<{
[sendMessageData.MessageId]: number,
[sendMessageData.Parameters]: [string | string[] | {
[file: string]: string;
}, MinifyOptions?
]
}>) {
minify(...e.data[sendMessageData.Parameters]).then(
// success handler - callback(id, SUCCESS(0), result)
// if `d` is transferable transfer zero-copy
d => {
port.postMessage([e.data[0], recieveMessageTypes.RESOLVE, d],
// @ts-ignore
[d].filter(x => (
(x instanceof ArrayBuffer) ||
(x instanceof MessagePort)
// || (self.ImageBitmap && x instanceof ImageBitmap)
)));
},
// error handler - callback(id, ERROR(1), error)
er => { postMessage([e.data[0], recieveMessageTypes.REJECT, '' + er]); }
);
};
};

View file

@ -0,0 +1,105 @@
export type FunctionMap = { [x: string]: Function }
export enum sendMessageData {
MessageId,
Function,
Parameters
}
export interface sendMessageMap <T> {
[sendMessageData.MessageId]: number,
[sendMessageData.Function]: number,
[sendMessageData.Parameters]: T[],
}
export enum recieveMessageTypes {
RESOLVE, // OK
REJECT // ERROR
}
export enum recieveMessageData {
MessageId,
MessageType,
Return
}
export interface recieveMessageMap <T> {
[recieveMessageData.MessageId]: number,
[recieveMessageData.MessageType]: recieveMessageTypes,
[recieveMessageData.Return]: T,
}
// // worker
// import { recieveMessageTypes, type FunctionMap } from "./util";
// function makeMessageHandler(functions: FunctionMap) {
// return (e) => {
// // Invoking within then() captures exceptions in the supplied async function as rejections
// Promise.resolve(e.data[1]).then(
// v => $$.apply($$, v)
// ).then(
// // success handler - callback(id, SUCCESS(0), result)
// // if `d` is transferable transfer zero-copy
// d => {
// postMessage([e.data[0], recieveMessageTypes.SUCCESS, d], [d].filter(x => (
// (x instanceof ArrayBuffer) ||
// (x instanceof MessagePort) ||
// (self.ImageBitmap && x instanceof ImageBitmap)
// )));
// },
// // error handler - callback(id, ERROR(1), error)
// er => { postMessage([e.data[0], recieveMessageTypes.ERROR, '' + er]); }
// );
// }
// }
// // host
// import { recieveMessageData, recieveMessageTypes, sendMessageData, type recieveMessageMap, type sendMessageMap } from "./util";
// function makeHostHandler(worker: Worker) {
// let currentId = 0;
// // Outward-facing promises store their "controllers" (`[request, reject]`) here:
// const promises: { [id: number]: { [t: number]: IArguments } } = {}
// ;
// /** Handle RPC results/errors coming back out of the worker.
// * Messages coming from the worker take the form `[id, status, result]`:
// * id - counter-based unique ID for the RPC call
// * status - 0 for success, 1 for failure
// * result - the result or error, depending on `status`
// */
// worker.onmessage = (e: MessageEvent<recieveMessageMap>) => {
// // invoke the promise's resolve() or reject() depending on whether there was an error.
// promises[e.data[0]][e.data[1]](e.data[2]);
// // ... then delete the promise controller
// promises[e.data[0]] = null;
// };
// // Return a proxy function that forwards calls to the worker & returns a promise for the result.
// return function () {
// let args = [].slice.call(arguments);
// return new Promise(function () {
// // Add the promise controller to the registry
// promises[++currentId] = arguments;
// // Send an RPC call to the worker - call(id, params)
// // The filter is to provide a list of transferables to send zero-copy
// let data: sendMessageMap<any> = {
// [sendMessageData.MessageId]: currentId,
// [sendMessageData.Function]: 1,
// [sendMessageData.Parameters]: args
// }
// worker.postMessage(data, args.filter(x => (
// (x instanceof ArrayBuffer) ||
// (x instanceof MessagePort) ||
// (self.ImageBitmap && x instanceof ImageBitmap)
// )));
// });
// };
// }

View file

@ -3,7 +3,7 @@
import SvelteSeo from "svelte-seo";
import { bookmarkify, parseMeta } from "./bookmarklets";
import type { Config } from "./config";
import { init } from "$lib/workers/terser";
import { SITE_URL } from '$lib/metadata';
/** @type {import('./$types').Snapshot<string>} */
@ -12,12 +12,14 @@
restore: (v: string) => (value = v),
};
let minify = init().minify
let value = "";
let output = "";
let options: Config = {};
async function process(str: string) {
options = await parseMeta(str);
let res = await bookmarkify(str, options);
let res = await bookmarkify(str, options, minify);
if (typeof res == "string") {
output = res;
}

View file

@ -2,14 +2,17 @@
import MagicString from "magic-string";
import { Parser } from "acorn";
import { minify } from "terser";
import { type MinifyOptions, type MinifyOutput } from "terser";
let sourceMap = false;
import { configSchema } from "./config.schema";
import type { Config } from "./config";
// console.log(configSchema)
export async function bookmarkify(code: string, options: Config) {
export async function bookmarkify(code: string, options: Config, minify: (files: string | string[] | {
[file: string]: string;
}, options?: MinifyOptions | undefined) => Promise<MinifyOutput>
) {
// try {
if (options.script) {

8
pnpm-lock.yaml generated
View file

@ -101,6 +101,9 @@ importers:
'@types/node':
specifier: ^20.12.7
version: 20.12.7
'@types/sharedworker':
specifier: ^0.0.115
version: 0.0.115
glob:
specifier: ^10.3.12
version: 10.3.12
@ -872,6 +875,9 @@ packages:
'@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
'@types/sharedworker@0.0.115':
resolution: {integrity: sha512-istxrCv9mbZQt7kXMVMsc4U+dbxG5y+ae5N+9f6pM9VOJmclF7FBWtTog9SMT16s1pZUTjga7ewaMVgqhDpTOg==}
'@types/unist@2.0.10':
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
@ -2841,6 +2847,8 @@ snapshots:
'@types/resolve@1.20.2': {}
'@types/sharedworker@0.0.115': {}
'@types/unist@2.0.10': {}
'@types/unist@3.0.2': {}