JavaScript API
Safe markup and classes
Return trusted HTML and expose class instances.
Kiln escapes values from your code by default. When a function or filter returns HTML you trust, mark it as safe. When you pass class instances to templates, choose which members templates can use.
Returning safe HTML
Wrap a string in safe() to output it without escaping:
import { createKiln, safe } from "@lucidkiln/core";
const kiln = createKiln({
loader,
filters: {
markdown: (text: string) => safe(renderMarkdown(text)),
},
});
<article>{{ post.body|markdown }}</article>
Only mark output safe when your code produced it or cleaned it. The second argument sets the strategy it is safe for, such as safe(value, "js"). It defaults to "html".
isSafe(value) checks whether a value is safe markup. Safe values are instances of the Markup class, and convert to strings as normal.
Class instances
Templates can read plain objects and arrays freely. A class instance exposes nothing until its class opts in, so templates can't reach methods or internals you didn't intend. List the members templates may use with the expose symbol:
import { expose } from "@lucidkiln/core";
class Post {
static [expose] = ["title", "url", "excerpt"] as const;
constructor(private data: PostData) {}
get title() {
return this.data.title;
}
get url() {
return `/blog/${this.data.slug}/`;
}
excerpt(length: number) {
return this.data.body.slice(0, length);
}
}
<a href="{{ post.url }}">{{ post.title }}</a>
<p>{{ post.excerpt(120) }}</p>
- Listed getters and fields can be read. Listed methods can be called.
exposeis read from the class and its parent classes. In a base class meant to be extended, type the list asreadonly string[]rather thanas const.- List
toStringto let templates print the object. It must return a string. Kiln calls it directly, neverSymbol.toPrimitiveorvalueOf. - Dates, sets and maps work without opting in.
Dynamic fields
For fields you don't know in advance, such as custom fields from a CMS, add a lookup method. It answers any name not in expose. Return undefined for a missing field.
import { expose, lookup } from "@lucidkiln/core";
class Post {
static [expose] = ["title"] as const;
[lookup](name: string): unknown {
return this.fields.get(name);
}
}
Names such as constructor, prototype and __proto__ stay blocked whatever expose and lookup say.