File size: 4,422 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
import $dedent from 'codedent';
import { unescape as $unescape } from 'html-escaper';
import { io } from './interpreter/_io.js';
/** @type {(tpl: string | TemplateStringsArray, ...values:any[]) => string} */
const dedent = $dedent;
/** @type {(value:string) => string} */
const unescape = $unescape;
const { isArray } = Array;
const { assign, create, defineProperties, defineProperty, entries } = Object;
const { all, resolve } = new Proxy(Promise, {
get: ($, name) => $[name].bind($),
});
const absoluteURL = (path, base = location.href) =>
new URL(path, base.replace(/^blob:/, '')).href;
/* c8 ignore start */
let id = 0;
const nodeInfo = (node, type) => ({
id: node.id || (node.id = `${type}-w${id++}`),
tag: node.tagName
});
/**
* Notify the main thread about element "readiness".
* @param {HTMLScriptElement | HTMLElement} target the script or custom-type element
* @param {string} type the custom/type as event prefix
* @param {string} what the kind of event to dispatch, i.e. `ready` or `done`
* @param {boolean} [worker = false] `true` if dispatched form a worker, `false` by default if in main
* @param {globalThis.CustomEvent} [CustomEvent = globalThis.CustomEvent] the `CustomEvent` to use
*/
const dispatch = (target, type, what, worker = false, CE = CustomEvent) => {
target.dispatchEvent(
new CE(`${type}:${what}`, {
bubbles: true,
detail: { worker },
})
);
};
export const createFunction = value => Function(`'use strict';return (${value})`)();
export const createResolved = (module, type, config, interpreter) => ({
type,
config,
interpreter,
io: io.get(interpreter),
run: (code, ...args) => module.run(interpreter, code, ...args),
runAsync: (code, ...args) => module.runAsync(interpreter, code, ...args),
runEvent: (...args) => module.runEvent(interpreter, ...args),
});
const dropLine0 = code => code.replace(/^(?:\n|\r\n)/, '');
export const createOverload = (module, name, before, after) => {
const method = module[name].bind(module);
module[name] = name === 'run' ?
// patch the sync method
(interpreter, code, ...args) => {
if (before) method(interpreter, before, ...args);
const result = method(interpreter, dropLine0(code), ...args);
if (after) method(interpreter, after, ...args);
return result;
} :
// patch the async one
async (interpreter, code, ...args) => {
if (before) await method(interpreter, before, ...args);
const result = await method(interpreter, dropLine0(code), ...args);
if (after) await method(interpreter, after, ...args);
return result;
};
};
export const js_modules = Symbol.for('polyscript.js_modules');
const jsModules = new Map;
defineProperty(globalThis, js_modules, { value: jsModules });
export const JSModules = new Proxy(jsModules, {
get: (map, name) => map.get(name),
has: (map, name) => map.has(name),
ownKeys: map => [...map.keys()],
});
const has = (_, field) => !field.startsWith('_');
const proxy = (modules, name) => new Proxy(
modules,
{ has, get: (modules, field) => modules[name][field] }
);
export const registerJSModules = (type, module, interpreter, modules) => {
// Pyodide resolves JS modules magically
if (type === 'pyodide') return;
// other runtimes need this pretty ugly dance (it works though)
const jsModules = 'polyscript.js_modules';
for (const name of Reflect.ownKeys(modules))
module.registerJSModule(interpreter, `${jsModules}.${name}`, proxy(modules, name));
module.registerJSModule(interpreter, jsModules, modules);
};
export const importJS = (source, name) => import(source).then(esm => {
jsModules.set(name, { ...esm });
});
export const importCSS = href => new Promise((onload, onerror) => {
if (document.querySelector(`link[href="${href}"]`)) onload();
document.head.append(
assign(
document.createElement('link'),
{ rel: 'stylesheet', href, onload, onerror },
)
)
});
export const isCSS = source => /\.css/i.test(new URL(source).pathname);
/* c8 ignore stop */
export {
dedent, unescape,
dispatch,
isArray,
assign,
create,
defineProperties,
defineProperty,
entries,
all,
resolve,
absoluteURL,
nodeInfo,
};
|