JavaScript API
TypeScript types
Generate types for render from your templates' props.
Kiln can write a TypeScript file that describes every template's props. With it, your editor completes template names in render, checks the data you pass, and knows which blocks renderBlock can render.
Generating types
Pass generateTypes your environment and the names of your templates. It returns the contents of a declaration file. Write it somewhere your tsconfig.json includes.
import { writeFile } from "node:fs/promises";
import { createKiln, generateTypes } from "@lucidkiln/core";
import { fileLoader } from "@lucidkiln/core/node";
const loader = fileLoader({ root: "src/templates" });
const kiln = createKiln({ loader });
const types = await generateTypes(kiln, await loader.list());
await writeFile("src/kiln.d.ts", types);
- Run it when templates change, for example as part of your build or dev server.
generateTypesdoesn't touch the file system, so you can run it anywhere, including on Workers.- Templates are listed in name order, so the file only changes when a template's props or blocks change.
- The file holds only types, so it adds nothing to your bundle.
The built-in loaders have a list method. For your own loader, see Listing templates.
What it checks
The file exports a Schema type. Pass it as the second type argument to createKiln. The first argument is your request state, or undefined when you have none:
// pages/post.kiln starts with {% props post: Post, related: Post[] = [] %}
import type { Schema } from "./kiln.js";
const kiln = createKiln<undefined, Schema<{ Post: PostView }>>({
loader,
types: { Post: (value) => value instanceof PostView },
});
await kiln.render("pages/post", { post }); // OK
await kiln.render("pages/post", { pots: post }); // Type error: "pots" isn't a prop
await kiln.render("pages/post"); // Type error: "post" is required
await kiln.renderBlock("pages/post", "content", { post }); // OK
await kiln.renderBlock("pages/post", "sidebar", { post }); // Type error: no such block
Names are checked the same way with or without .kiln. A name that isn't in the file, such as one built at runtime, still works. Kiln can't know which template it is, so it accepts any data.
Each environment has its own schema. Generate a file for each template root and import its Schema under a different name, such as SiteSchema and EmailSchema. They can describe the same template name with different props without affecting each other. A schema applies to render, renderBlock and renderStream.
How the data type is worked out
- A template with
propsaccepts exactly those props. This matches strict mode, which rejects anything else. A rest prop lets it accept any other key too, and then it must also pass the props its layouts require, unless it sets them at its top level. - A prop with a default is optional, and also accepts null.
- A page without
propsaccepts any data. If its layout declares required props, the page must pass them, unless it sets them at its top level. - An untyped prop accepts any value.
Kiln's types become these TypeScript types:
| Kiln | TypeScript |
|---|---|
null, boolean, number | The same |
string | string | Markup |
markup | Markup |
array | readonly unknown[] |
hash | A plain object or a Map |
set | ReadonlySet<unknown> |
date | Date |
object | object |
"primary", 2 | The same literal |
string[] | readonly (string | Markup)[] |
{ name: string, url?: string } | { name: string | Markup; url?: string | Markup } |
Post | The type you declare for Post |
Your own types
A prop type such as Post is one your app defines. Give it two things:
- A TypeScript type, by passing a map of your types to the generated
Schema. - A check that runs when a template receives the value, in the
typesoption.
import { createKiln } from "@lucidkiln/core";
import type { Schema } from "./kiln.js";
type HostTypes = { Post: PostView };
const kiln = createKiln<undefined, Schema<HostTypes>>({
loader,
types: {
Post: (value) => value instanceof PostView,
},
});
- Type names start with a capital letter, followed by letters, digits or underscores.
- A check gets the value and returns true when it fits. It must not be async.
- When the schema lists a host type, TypeScript requires a check for it that narrows to the right type.
- Checks run in strict mode only, like every other prop check. See How values are checked.
- A type with a check but no entry in your type map is
unknownin the generated file.
For project-wide defaults, you can still add to the exported KilnTemplates and KilnTypes interfaces through TypeScript module augmentation. Environments without an explicit schema use those defaults. An explicit schema uses only its own template and host-type maps.