pagez

The template value

What html makes. It records its strings and values and renders nothing, and render.ts turns it into a string. html exists only at build time, and an element's browser <script> that imports it fails.

A value is marked with a registered symbol, not a class. A template loads Pagez through Vite's module runner, and the build loads it through Node. That makes two copies of this file, and instanceof fails between them (elements.md).

const TEMPLATE: unique symbol = Symbol.for("pagez.template");
const RAW: unique symbol = Symbol.for("pagez.raw");

/** A template `html` recorded at build time, not yet rendered. */
export interface Template {
  readonly [TEMPLATE]: true;
  readonly strings: readonly string[];
  readonly values: readonly unknown[];
  /** Where `html` was called, whose stack is read only if a value fails, to name its line. */
  readonly called?: Error;
}

/** HTML that goes into a page as it is. */
export interface Raw {
  readonly [RAW]: true;
  readonly value: string;
}

/** Records a template for the build's renderer, escaping nothing yet. */
export function html(strings: TemplateStringsArray, ...values: unknown[]): Template {
  return { [TEMPLATE]: true, strings, values, called: new Error() };
}

HTML that is already safe, which only the renderer makes, from a nested html template. A site has no way to write it, so every value it interpolates is escaped.

export function raw(value: string): Raw {
  return { [RAW]: true, value };
}

export const isTemplate = (value: unknown): value is Template =>
  (value as Template | null)?.[TEMPLATE] === true;

export const isRaw = (value: unknown): value is Raw => (value as Raw | null)?.[RAW] === true;