Skip to Content
WidgetAPI Reference

API reference

@evanion/widget 0.1.0 and its patch releases up to 0.1.3, the version on npm. Values on this page were produced by running the source at f9b1d93, a later commit than the 0.1.0 tag, and are not re-executed.

Everything @evanion/widget exports, one heading per symbol. Both renderers re-export every symbol here except warnOnce and resetWarnings, so a renderer’s users import these names from the renderer.

defineWidgets

function defineWidgets<R extends WidgetRegistry>(registry: R): R;
Signature — a shape, not a call

Returns the registry it was handed, typed as the literal object passed in.

import { , } from '@evanion/widget'; // Whatever your renderer resolves a type to. The core reads the keys and never // calls a value, so a stand-in is enough to show what the keys do. const = () => null; const = () => null; const = ({ : , : }); const = ( [{ : 'wingspan', : 'listing', : { : 'Wingspan' } }], , ); ; // -> []

Annotating the same object as WidgetRegistry widens its keys to string, which takes away the key union a code editor completes on and the union a renderer types a component against. The key union has no effect on validateItems, whose required map is keyed by plain strings.

validateItems

function validateItems( items: unknown, known: KnownWidgetTypes, required?: Record<string, string[]>, ): WidgetProblem[];
Signature — a shape, not a call

Walks items, recurses into each item’s children, and returns every problem it found. It throws nothing and it short-circuits nothing, so one call reports a whole payload.

ParameterWhat it takes
itemsthe payload, typed unknown because it usually arrives as parsed JSON
knowna registry, or a plain list of type names
requireda widget type mapped to the props that must be present and non-blank
import { } from '@evanion/widget'; import type { } from '@evanion/widget'; // The listing page as the CMS saved it, after the shelf's type was renamed. const : [] = [ { : 'brass-birmingham', : 'listing', : { : 'Brass: Birmingham', : 4 }, }, { : 'tonight', : 'featured-shelf', : { : 'On the table tonight' }, }, ]; // The widget types the shop's renderer has a component for. const = ['listing', 'shelf']; const = (, ); ; // -> [{ index: 1, id: 'tonight', type: 'featured-shelf', message: 'unknown widget type' }]

A type matches when it is an own key of a registry or an entry of a list, so an item typed constructor or __proto__ is unknown. Ids are checked for duplicates within one sibling list only. What it reports lists every message and what produces it.

warnOnce

function warnOnce(message: string): void;
Signature — a shape, not a call

Writes message to console.warn the first time it is seen and stays quiet every time after, for the life of the process. It writes nothing at all when NODE_ENV is production, and a bundler folds that comparison and drops the call.

A renderer warns from render, so one item whose type the registry lacks would otherwise log on every re-render, and twice over on a page that renders on the server and then hydrates. Every string in ERROR_MESSAGES carries the offending item’s type and id, which is what makes the text a usable key: each bad item is reported once, and a second bad item is still reported.

A renderer calls warnOnce. A project that draws through a published renderer has no reason to call it, and neither renderer re-exports it. Your own renderer runs a minimal renderer that calls it.

resetWarnings

function resetWarnings(): void;
Signature — a shape, not a call

Clears the set of messages warnOnce has already written. A set that lives as long as the process is what a dev server wants and what a test file cannot have, since one case’s warning would silence the next case that produces the same message. Each renderer’s test setup calls this before every test, and the renderer that Your own renderer builds calls it before its first render.

ERROR_MESSAGES

The text a renderer warns with. Held here so that a widget type the registry lacks reads the same way whether React or Astro drew the page.

import { } from '@evanion/widget'; const = .('listting', 'hive'); ; // -> 'Unknown widget type "listting" for widget ID "hive". Skipping render.'
KeyShapeWarned whenPrinted by
UNKNOWN_WIDGET(type, id) => stringan item’s type is not a key of the registryboth renderers
MALFORMED_ITEMSstringthe items the renderer is handed are not an arrayboth renderers
MALFORMED_ITEM(id, type) => stringan entry is not an object, or carries no usable type@evanion/react-widget; @evanion/astro-widget prints UNKNOWN_WIDGET for this item
MALFORMED_CHILDREN(id) => stringan item’s children is present and is not an array@evanion/react-widget; an Astro widget receives children as data
UNKNOWN'unknown'the stand-in a message uses for an id or type it lacks@evanion/astro-widget, inside UNKNOWN_WIDGET

MALFORMED_ITEM takes (id, type), the reverse of UNKNOWN_WIDGET’s (type, id).

VALIDATION_MESSAGES

The text validateItems reports. Compare a problem’s message against these to group, count or translate a report, and assert against them in a test. Every key and what produces it is on What it reports.

MISSING_FIELD is (field: string) => string; the other eight are plain strings.

AnyWidgetItem

interface AnyWidgetItem<Type extends string = string, Props = object> { id: string; type: Type; props: Props; meta?: WidgetMeta; children?: AnyWidgetItem[]; }
Signature — a shape, not a call

One item, typed without a registry: a content management system payload, a fixture, a network response. This is the whole item model, and every renderer draws exactly this. The Overview walks through each field. @evanion/react-widget also exports WidgetItem<C>, which checks an item’s props against the component its registry C holds for the item’s type.

import { } from '@evanion/widget'; import type { } from '@evanion/widget'; const : [] = [ { : 'brass-birmingham', : 'listing', : { : 'Brass: Birmingham', : 4 }, : { : 2 }, }, { : 'tonight', : 'shelf', : { : 'On the table tonight' }, : [ { : 'wingspan', : 'listing', : { : 'Wingspan' } }, ], }, ]; const = (, ['listing', 'shelf']); ; // -> []

WidgetRegistry

type WidgetRegistry<T = unknown> = Record<string, T>;
Signature — a shape, not a call

A widget type mapped to whatever the renderer resolves it to. T is the renderer’s component type. @evanion/react-widget instantiates it at a ComponentType, which is what drives its compile-time prop check. @evanion/astro-widget leaves it at the default, because an .astro file’s default export carries no prop types: its props live in the file’s frontmatter Props interface. An Astro project gets no compile-time prop check, so a validateItems call with required, run by the project’s own build script, is the check it has.

WidgetMeta

type WidgetMeta = Record<string, unknown>;
Signature — a shape, not a call

The type of an item’s meta when the item chrome declares no narrower one. The item chrome is the component a renderer draws around each item, which the application supplies as chrome.item. In @evanion/react-widget, item chrome typed WidgetItemComponent<M> narrows every item’s meta to M. WidgetMeta accepts any object, so an item carries whatever placement data it likes and item chrome that reads meta narrows it by hand.

WidgetProblem

interface WidgetProblem { index: number; id: string; type: string; message: string; }
Signature — a shape, not a call

One line of the report validateItems returns. index is the item’s position within its own sibling list, and -1 when the payload itself is not a list. id and type are the item’s own, or - when the item carries none a reader could use.

KnownWidgetTypes

type KnownWidgetTypes = WidgetRegistry | readonly string[];
Signature — a shape, not a call

The set of widget types a payload may use. A plain list of names is accepted beside a registry so that a webhook handler or a continuous integration script validates a payload without importing the components it will never draw.

Last updated on