/**
* @file stringRenderer.js
* @description The seam that lets the string renderer leave a bundle.
*
* ## Why this exists
*
* The string renderer is the pre-IR rendering path: render the whole template
* to HTML, parse it, diff the result into the document, then re-scan the
* subtree for lists, deferred blocks and event handlers. It is about 87 KB of
* source -- roughly a quarter of a small application's bundle -- and since the
* IR lowers `<@if>`, `<@for>`, `<slot>`, `<@defer>` and component tags, most
* applications never execute a line of it.
*
* They were still paying for it, because `AvenxComponent` imported the four
* classes directly and an import is reachability. The classes were already
* behind lazy getters, so nothing was *constructed* -- but nothing was dropped
* either.
*
* So the import moved. `AvenxComponent` asks this registry, and the compiler
* adds the module that fills it only when at least one component in the build
* actually fell back. An application whose every template compiles does not
* reference the string renderer, and the bundler shakes it out.
*
* ## Deliberately tiny
*
* This module imports nothing. If it imported the renderer to provide a
* default, the renderer would be reachable again and the whole arrangement
* would achieve nothing.
*
* ## Temporary
*
* This is migration scaffolding. It exists for as long as there are template
* constructs the IR does not model -- suspense, error boundaries, deadlock
* boundaries, transitions, refs, declarative validation and dynamic component
* tags. When the last of those lowers, the string renderer goes and this module
* goes with it.
* @module lib/core/renderer/stringRenderer
*/
import { AvenxError, AvenxErrorCodes } from '../runtime/AvenxError.js';
/**
* The renderer classes, once something has installed them.
* @type {{DomPatcher: Function, ListManager: Function, DeferManager: Function,
* TemplateRenderer: Function}|null}
*/
let installed = null;
/**
* Registers the string renderer's classes.
*
* Called by `avenx-core/runtime/string-renderer`, which the compiler adds to
* the graph when a component falls back.
* @param {object} classes - The renderer classes.
* @param {Function} classes.DomPatcher - The DOM patcher.
* @param {Function} classes.ListManager - The list manager.
* @param {Function} classes.DeferManager - The defer manager.
* @param {Function} classes.TemplateRenderer - The template renderer.
*/
export function installStringRenderer(classes) {
installed = classes;
}
/**
* Whether the string renderer is available in this bundle.
* @returns {boolean} True when something installed it.
*/
export function hasStringRenderer() {
return installed !== null;
}
/**
* Returns the string renderer's classes.
*
* Throws rather than returning null when nothing installed it, because the
* caller is a component about to render and has no second option. Reaching
* here means a component without a render program ended up in a build that
* concluded no component needed one, which is a compiler fault and should read
* like one rather than like a missing method on undefined.
*
* The code is AVX_R34 and not the generic template-render error it used to
* borrow. Reusing AVX_R08 made this read as "an interpolation failed", which
* sent a developer looking at their expression -- the one place the fault was
* certainly not. It also made the condition invisible to `avenx explain`,
* because AVX_R08's entry describes something else entirely.
* @param {string} [componentName] - The component that needs the renderer, for
* the diagnostic.
* @returns {object} The renderer classes.
*/
export function requireStringRenderer(componentName = 'a component') {
if (!installed) {
throw new AvenxError(
AvenxErrorCodes.RENDERER_UNAVAILABLE,
componentName,
'the string renderer',
);
}
return installed;
}