|
'use strict'; |
|
const fetch = (m => m.__esModule ? m.default : m )(require('@webreflection/fetch')); |
|
const { $ } = require('basic-devtools'); |
|
|
|
const $xworker = (require('./worker/class.js')); |
|
const workerURL = (require('./worker/url.js')); |
|
const { getRuntime, getRuntimeID } = require('./loader.js'); |
|
const { registry } = require('./interpreters.js'); |
|
const { JSModules, all, dispatch, resolve, defineProperty, nodeInfo, registerJSModules } = require('./utils.js'); |
|
|
|
const getRoot = (script) => { |
|
let parent = script; |
|
while (parent.parentNode) parent = parent.parentNode; |
|
return parent; |
|
}; |
|
|
|
const queryTarget = (script, idOrSelector) => { |
|
const root = getRoot(script); |
|
return root.getElementById(idOrSelector) || $(idOrSelector, root); |
|
}; |
|
exports.queryTarget = queryTarget; |
|
|
|
const targets = new WeakMap(); |
|
const targetDescriptor = { |
|
get() { |
|
let target = targets.get(this); |
|
if (!target) { |
|
target = document.createElement(`${this.type}-script`); |
|
targets.set(this, target); |
|
handle(this); |
|
} |
|
return target; |
|
}, |
|
set(target) { |
|
if (typeof target === 'string') |
|
targets.set(this, queryTarget(this, target)); |
|
else { |
|
targets.set(this, target); |
|
handle(this); |
|
} |
|
}, |
|
}; |
|
|
|
const handled = new WeakMap(); |
|
|
|
const interpreters = new Map(); |
|
exports.interpreters = interpreters; |
|
|
|
const execute = async (currentScript, source, XWorker, isAsync) => { |
|
const { type } = currentScript; |
|
const module = registry.get(type); |
|
|
|
if (module.experimental) |
|
console.warn(`The ${type} interpreter is experimental`); |
|
const [interpreter, content] = await all([ |
|
handled.get(currentScript).interpreter, |
|
source, |
|
]); |
|
try { |
|
|
|
|
|
defineProperty(document, 'currentScript', { |
|
configurable: true, |
|
get: () => currentScript, |
|
}); |
|
registerJSModules(type, module, interpreter, JSModules); |
|
module.registerJSModule(interpreter, 'polyscript', { |
|
XWorker, |
|
currentScript, |
|
js_modules: JSModules, |
|
}); |
|
dispatch(currentScript, type, 'ready'); |
|
const result = module[isAsync ? 'runAsync' : 'run'](interpreter, content); |
|
const done = dispatch.bind(null, currentScript, type, 'done'); |
|
if (isAsync) result.then(done); |
|
else done(); |
|
return result; |
|
} finally { |
|
delete document.currentScript; |
|
} |
|
|
|
}; |
|
|
|
const getValue = (ref, prefix) => { |
|
const value = ref?.value; |
|
return value ? prefix + value : ''; |
|
}; |
|
|
|
const getDetails = (type, id, name, version, config, configURL, runtime = type) => { |
|
if (!interpreters.has(id)) { |
|
const details = { |
|
interpreter: getRuntime(name, config, configURL), |
|
queue: resolve(), |
|
XWorker: $xworker(type, version), |
|
}; |
|
interpreters.set(id, details); |
|
|
|
|
|
|
|
if (!interpreters.has(type)) interpreters.set(type, details); |
|
if (!interpreters.has(runtime)) interpreters.set(runtime, details); |
|
|
|
} |
|
return interpreters.get(id); |
|
}; |
|
exports.getDetails = getDetails; |
|
|
|
|
|
|
|
|
|
const handle = async (script) => { |
|
|
|
|
|
if (handled.has(script)) { |
|
const { target } = script; |
|
if (target) { |
|
|
|
if (script.closest('head')) document.body.append(target); |
|
|
|
else script.after(target); |
|
} |
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
const { |
|
attributes: { async: isAsync, config, env, target, version }, |
|
src, |
|
type, |
|
} = script; |
|
|
|
const versionValue = version?.value; |
|
const name = getRuntimeID(type, versionValue); |
|
let configValue = getValue(config, '|'); |
|
const id = getValue(env, '') || `${name}${configValue}`; |
|
configValue = configValue.slice(1); |
|
|
|
|
|
const url = workerURL(script); |
|
if (url) { |
|
const XWorker = $xworker(type, versionValue); |
|
const xworker = new XWorker(url, { |
|
...nodeInfo(script, type), |
|
async: !!isAsync, |
|
config: configValue |
|
}); |
|
handled.set( |
|
defineProperty(script, 'xworker', { value: xworker }), |
|
{ xworker } |
|
); |
|
return; |
|
} |
|
|
|
|
|
const targetValue = getValue(target, ''); |
|
const details = getDetails(type, id, name, versionValue, configValue); |
|
|
|
handled.set( |
|
defineProperty(script, 'target', targetDescriptor), |
|
details, |
|
); |
|
|
|
if (targetValue) targets.set(script, queryTarget(script, targetValue)); |
|
|
|
|
|
const source = src ? fetch(src).text() : script.textContent; |
|
details.queue = details.queue.then(() => |
|
execute(script, source, details.XWorker, !!isAsync), |
|
); |
|
} |
|
}; |
|
exports.handle = handle; |
|
|