import { EvtRt } from '../EvtRt.js'; import { MountConfig, MountContext } from '../types/mount-observer/types.js'; import '../ElementMountExtension.js'; /** * Handler for Mount Observer Script Elements (MOSEs). * Processes script[type="mountobserver"] elements to declaratively configure mount observers. * * Supports two modes: * 1. External JSON: * 2. Inline JSON: * * Supports multiple configs in one script element: * - Single config: { "do": "builtIns.hoistTemplate" } * - Multiple configs: [{ "do": "builtIns.hoistTemplate" }, { "do": "builtIns.HTMLInclude" }] */ export class MountObserverScriptHandler extends EvtRt { // Static properties define default MountConfig constraints static matching = 'script[type="mountobserver"]'; static whereInstanceOf = HTMLScriptElement; async mount(mountedElement: Element, MountConfig: MountConfig, context: MountContext): Promise { this.abort(); // Clean up event listeners (one-time operation) const scriptElement = mountedElement as HTMLScriptElement; let config = (scriptElement as any).export; if (!config) { // Check if script has src attribute const srcAttr = scriptElement.getAttribute('src'); if (srcAttr) { // External JSON mode: import from src //const resolvedUrl = new URL(srcAttr, document.baseURI).href; try { const module = await import(srcAttr, { with: { type: 'json' } } as any); config = module.default; } catch (error) { throw new Error(`Failed to import JSON from '${srcAttr}': ${error instanceof Error ? error.message : String(error)}`); } } else { // Inline JSON mode: parse textContent const jsonText = scriptElement.textContent?.trim(); if (!jsonText) { throw new Error('Script element must have either src attribute or JSON content'); } try { config = JSON.parse(jsonText); } catch (error) { throw new Error(`Failed to parse JSON content: ${error instanceof Error ? error.message : String(error)}`); } } // Validate that config is an object or array if (typeof config !== 'object' || config === null) { throw new Error('Mount observer config must be an object or array'); } // Store the parsed config on the script element's export property (scriptElement as any).export = config; // Dispatch resolved event const { ResolvedEvent } = await import('../Events.js'); scriptElement.dispatchEvent(new ResolvedEvent(config)); } // Handle array of configs if (Array.isArray(config)) { // Mount each config in the array for (const singleConfig of config) { if (typeof singleConfig !== 'object' || singleConfig === null) { throw new Error('Each config in array must be an object'); } await scriptElement.mount(singleConfig); } } else { // Single config object - mount it await scriptElement.mount(config); } } } // Register built-in handler import { MountObserver } from '../MountObserver.js'; export const mos = 'builtIns.mountObserverScript'; MountObserver.define(mos, MountObserverScriptHandler);