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;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[];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.
| Parameter | What it takes |
|---|---|
items | the payload, typed unknown because it usually arrives as parsed JSON |
known | a registry, or a plain list of type names |
required | a 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;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;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.'| Key | Shape | Warned when | Printed by |
|---|---|---|---|
UNKNOWN_WIDGET | (type, id) => string | an item’s type is not a key of the registry | both renderers |
MALFORMED_ITEMS | string | the items the renderer is handed are not an array | both renderers |
MALFORMED_ITEM | (id, type) => string | an 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) => string | an 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[];
}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>;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>;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;
}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[];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.