@itsy/html/util
import { join, map, range, when, choose, wrap, comment } from '@itsy/html/util';Opt-in helpers with no shared state, one export each, so a bundler keeps only what is imported.
Everything except comment returns a plain array or the result of a thunk. Nothing here renders markup or escapes anything; the template it lands in does that, for the context it lands in.
join
join<I, J>(items: Iterable<I> | undefined, joiner: J): (I | J)[]Puts joiner between the items.
html`<p>${join(tags.map(Tag), ', ')}</p>`;The joiner is a value like any other, so a string is escaped and an Html is not. undefined items give an empty array.
map
map<T>(items: Iterable<T> | undefined, f: (item: T, index: number) => Renderable): Renderable[]Array.prototype.map for anything iterable, with an index.
html`<ul>${map(ids, (id, i) => html`<li>${i}: ${id}</li>`)}</ul>`;TIP
This is for a Set, a Map.values() or a generator — things with no .map of their own. For an array, call .map and skip the import.
range
range(end: number): number[]
range(start: number, end: number, step?: number): number[]The integers from start up to but not including end. One argument means range(0, end). A negative step counts down.
range(5); // [0, 1, 2, 3, 4]
range(1, 10, 3); // [1, 4, 7]
html`<p>${map(range(5), Star)}</p>`;when
when<T, F>(condition: unknown, trueFn: () => T, falseFn?: () => F): T | F | undefinedOne branch or the other, and only the chosen branch is built.
html`${when(user, () => Profile(user), () => Login())}`;With no falseFn, a false condition renders nothing - so it is usually cleaner to just do && in those cases.
choose
choose<T, V>(value: T, cases: readonly (readonly [T, () => V])[], fallback?: () => V): V | undefinedA switch as an expression. The first case that matches (strict equivalence) will be rendered.
html`${choose(status, [
['ok', () => Ok()],
['err', () => Err()],
], () => Unknown())}`;wrap
wrap(items: Iterable<Renderable>, tag: string, attributes?: Record<string, AttrValue>): Renderable[]Each item inside a <tag>, with optional attributes.
html`<ul>${wrap(['a', 'b'], 'li', { class: 'name' })}</ul>`;<ul><li class="name">a</li><li class="name">b</li></ul>Items are rendered by the surrounding template, so text is escaped and Html is not. Attributes go through attrs() with the default URL guard, not one from createHtml. A tag name that is not a legal tag name throws code 17.
Inside <script> and <style> every item must itself be Html, and anything else throws code 6: escaping keeps text from ending the block, but not from running.
comment
comment(text: string): HtmlAn HTML comment the text cannot close early.
html`${comment('closes early? --> no')}`;<!-- closes early? - -> no -->