preserves/implementations/javascript/packages/schema/preserves-schema-browser.js

101 lines
3.5 KiB
JavaScript

(() => {
const I = new PreservesSchema.SchemaInterpreter();
globalThis.Schema = { __interpreter: I };
let schemaReady;
globalThis.SchemaReady = new Promise(res => schemaReady = res);
async function translateScripts() {
const schemaScripts =
Array.from(document.getElementsByTagName('script'))
.filter(s => (s.type === 'text/preserves+schema' ||
s.type === 'schema'));
for (const script of schemaScripts) {
function complain(message, detail) {
const e = new Error(message);
e.script = script;
e.detail = detail;
console.error(e);
}
let sourceCodeBlob;
const sourceUrl = script.src || script.getAttribute('data-src') || false;
if (sourceUrl) {
const res = await fetch(sourceUrl);
if (res.ok) {
sourceCodeBlob = new Uint8Array(await res.arrayBuffer());
} else {
complain(`Failed to retrieve schema from ${sourceUrl}`, { res });
continue;
}
} else {
sourceCodeBlob = new TextEncoder().encode(script.innerHTML);
}
const schemaName = () => {
const n = script.getAttribute('name');
if (n === null) complain(`<script type="schema"> must have name attribute`);
return n;
};
if (sourceCodeBlob[0] >= 128) {
// Binary Preserves blob
const value = Preserves.decode(sourceCodeBlob);
const bundle = PreservesSchema.Meta.toBundle(value);
if (bundle !== void 0) {
const prefixStr = script.getAttribute('prefix');
const bundlePrefix = (prefixStr ? prefixStr.split('.') : []).map(Symbol.for);
bundle.modules.forEach((schema, path) => {
const modulePath = [... bundlePrefix, ... path];
I.env.set(modulePath, schema);
});
} else {
const schema = PreservesSchema.Meta.toSchema(value);
if (schema !== void 0) {
const modulePath = schemaName().split('.').map(Symbol.for);
I.env.set(modulePath, schema);
}
}
} else {
// Presumably text
const sourceCode = new TextDecoder('utf-8', { fatal: true }).decode(sourceCodeBlob);
const name = schemaName();
const schema = PreservesSchema.readSchema(sourceCode, { name });
const modulePath = name.split('.').map(Symbol.for);
I.env.set(modulePath, schema);
}
}
// I.env.forEach((_schema, path) => console.log(PreservesSchema.Meta.formatModulePath(path)));
for (const [modulePath, schema] of I.env) {
let container = Schema;
modulePath.slice(0, -1).forEach(n => {
if (!(n.description in container)) container[n.description] = {};
container = container[n.description];
});
container[modulePath[modulePath.length - 1].description] = moduleFor(modulePath, schema);
}
schemaReady();
}
function moduleFor(modulePath, schema) {
const { Meta, GenType, Type } = PreservesSchema;
const mod = {};
schema.definitions.forEach((d, n) => {
const definitionName = n.description;
const definitionId = Meta.jsId(definitionName);
mod[`${definitionId}`] = I.definitionConstructor(modulePath, n);
mod[`from${definitionId}`] = I.unparser(modulePath, n);
mod[`to${definitionId}`] = v => I.tryParse(modulePath, n, v);
mod[`as${definitionId}`] = v => I.parse(modulePath, n, v);
});
return mod;
}
window.addEventListener('DOMContentLoaded', translateScripts);
})();