perf(csr): load component controllers on demand
This commit is contained in:
+119
-5
@@ -17,12 +17,14 @@ export { NAV_RUNTIME } from "./nav-runtime.ts";
|
||||
export { REALTIME_RUNTIME } from "./realtime-runtime.ts";
|
||||
export { ACTION_RUNTIME } from "./action-runtime.ts";
|
||||
|
||||
/** The reactive runtime served at `/__wrnexus/reactive.js` (plain browser JS). */
|
||||
export function getReactiveRuntime(development = false): string {
|
||||
const CONTROLLER_SECTIONS = ["PRIMARY", "UI", "PIN"] as const;
|
||||
|
||||
function runtimeForMode(development: boolean): string {
|
||||
if (development) {
|
||||
return REACTIVE_RUNTIME
|
||||
.replace(/\/\*__WRNEXUS_DEV_START__\*\//g, "")
|
||||
.replace(/\/\*__WRNEXUS_DEV_END__\*\//g, "");
|
||||
return REACTIVE_RUNTIME.replace(/\/\*__WRNEXUS_DEV_START__\*\//g, "").replace(
|
||||
/\/\*__WRNEXUS_DEV_END__\*\//g,
|
||||
"",
|
||||
);
|
||||
}
|
||||
return REACTIVE_RUNTIME.replace(
|
||||
/\/\*__WRNEXUS_DEV_START__\*\/[\s\S]*?\/\*__WRNEXUS_DEV_END__\*\//g,
|
||||
@@ -30,6 +32,118 @@ export function getReactiveRuntime(development = false): string {
|
||||
);
|
||||
}
|
||||
|
||||
function controllerSection(source: string, name: (typeof CONTROLLER_SECTIONS)[number]): string {
|
||||
const pattern = new RegExp(
|
||||
`/\\*__WRNEXUS_CONTROLLERS_${name}_START__\\*/([\\s\\S]*?)/\\*__WRNEXUS_CONTROLLERS_${name}_END__\\*/`,
|
||||
);
|
||||
const match = pattern.exec(source);
|
||||
if (!match?.[1]) throw new Error(`Missing reactive runtime controller section: ${name}`);
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function stripControllerSections(source: string): string {
|
||||
for (const name of CONTROLLER_SECTIONS) {
|
||||
source = source.replace(
|
||||
new RegExp(
|
||||
`/\\*__WRNEXUS_CONTROLLERS_${name}_START__\\*/[\\s\\S]*?/\\*__WRNEXUS_CONTROLLERS_${name}_END__\\*/`,
|
||||
),
|
||||
"",
|
||||
);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
const CONTROLLER_LOADER = String.raw`
|
||||
var componentControllerSelector = "[data-wrn-anchored],[data-wrn-dialog],[data-wrn-roving],[data-wrn-scrollspy],[data-wrn-splitter],[data-wrn-navbar],[data-wrn-preferences],[data-wrn-select],[data-wrn-pin-input]";
|
||||
var componentControllerPromise = null;
|
||||
var componentControllerUrl = (document.currentScript && document.currentScript.src)
|
||||
? new URL("./controllers.js", document.currentScript.src).href
|
||||
: "/__wrnexus/controllers.js";
|
||||
function needsComponentControllers(root) {
|
||||
var host = root || document;
|
||||
return !!((host.matches && host.matches(componentControllerSelector)) ||
|
||||
(host.querySelector && host.querySelector(componentControllerSelector)));
|
||||
}
|
||||
function loadComponentControllers(root) {
|
||||
if (!needsComponentControllers(root)) return;
|
||||
if (window.__wrnexusComponentControllers) {
|
||||
window.__wrnexusComponentControllers.hydrate(root || document);
|
||||
return;
|
||||
}
|
||||
if (!componentControllerPromise) {
|
||||
componentControllerPromise = new Promise(function (resolve, reject) {
|
||||
var script = document.createElement("script");
|
||||
script.src = componentControllerUrl;
|
||||
script.defer = true;
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
componentControllerPromise.then(function () {
|
||||
if (window.__wrnexusComponentControllers) window.__wrnexusComponentControllers.hydrate(root || document);
|
||||
}).catch(function (error) { console.error("[wrnexus] failed to load component controllers", error); });
|
||||
}
|
||||
`;
|
||||
|
||||
/** The reactive runtime served at `/__wrnexus/reactive.js` (plain browser JS). */
|
||||
export function getReactiveRuntime(development = false): string {
|
||||
let source = stripControllerSections(runtimeForMode(development));
|
||||
source = source.replace(" function hydrateScopes(root) {", `${CONTROLLER_LOADER}\n function hydrateScopes(root) {`);
|
||||
source = source.replace(
|
||||
" hydrateNavbarControllers(host);\n hydratePreferenceControllers(host);\n hydrateSelectControllers(host);\n hydratePinInputControllers(host);",
|
||||
" loadComponentControllers(host);",
|
||||
);
|
||||
source = source.replace(" startDocumentWatch();\n", "");
|
||||
source = source.replace(
|
||||
" setupAnchoredOverlays();\n setupModalDialogs();\n setupRovingFocus();\n setupScrollspy();\n setupSplitters();",
|
||||
" loadComponentControllers(document);",
|
||||
);
|
||||
source = source.replace(
|
||||
" window.__wrnexusRepositionAnchored = repositionAnchored;",
|
||||
" window.__wrnexusRepositionAnchored = function (root) { if (window.__wrnexusComponentControllers) window.__wrnexusComponentControllers.reposition(root); };",
|
||||
);
|
||||
source = source.replace(
|
||||
" window.__wrnexusMountClientRoots = mountClientRoots;",
|
||||
` window.__wrnexusControllerBridge = { invokeComponentOutput: invokeComponentOutput, callServerFunction: callServerFunction, dispatchComponentEvent: dispatchComponentEvent, emitPinInputEvent: emitPinInputEvent, parseScopeDecl: parseScopeDecl, warn: ${development ? "warnOnce" : "function () {}"} };\n window.__wrnexusMountClientRoots = mountClientRoots;`,
|
||||
);
|
||||
return source;
|
||||
}
|
||||
|
||||
/** Component-specific controllers, loaded only when their marker is present. */
|
||||
export function getComponentControllerRuntime(development = false): string {
|
||||
const source = runtimeForMode(development);
|
||||
const sections = CONTROLLER_SECTIONS.map((name) => controllerSection(source, name)).join("\n");
|
||||
return `
|
||||
(function () {
|
||||
"use strict";
|
||||
var bridge = window.__wrnexusControllerBridge || {};
|
||||
var invokeComponentOutput = bridge.invokeComponentOutput;
|
||||
var callServerFunction = bridge.callServerFunction;
|
||||
var dispatchComponentEvent = bridge.dispatchComponentEvent;
|
||||
var emitPinInputEvent = bridge.emitPinInputEvent;
|
||||
var parseScopeDecl = bridge.parseScopeDecl;
|
||||
var warnOnce = bridge.warn || function () {};
|
||||
${sections}
|
||||
function hydrate(root) {
|
||||
var host = root || document;
|
||||
hydrateNavbarControllers(host);
|
||||
hydratePreferenceControllers(host);
|
||||
hydrateSelectControllers(host);
|
||||
hydratePinInputControllers(host);
|
||||
syncRovingGroups(host);
|
||||
}
|
||||
setupAnchoredOverlays();
|
||||
setupModalDialogs();
|
||||
setupRovingFocus();
|
||||
setupScrollspy();
|
||||
setupSplitters();
|
||||
window.__wrnexusComponentControllers = { hydrate: hydrate, reposition: repositionAnchored };
|
||||
hydrate(document);
|
||||
})();
|
||||
`.trim();
|
||||
}
|
||||
|
||||
/** The client-side navigation runtime served at `/__wrnexus/nav.js`. */
|
||||
export function getNavRuntime(): string {
|
||||
return NAV_RUNTIME;
|
||||
|
||||
Reference in New Issue
Block a user