Transform
Parse Markdown once, at build time.
transform runs on each document at build time, after the schema. Do the expensive work here, eg rendering Markdown, so pages only read the result.
Returning metadata and body
transform receives the source, { slug, metadata, body, file }, and returns a new metadata and/or body. Whatever it leaves out stays as it was, and the types of what it returns become the document's.
import { marked } from "marked";
export default defineConfig({
collections: {
posts: {
loader: directory("content/posts"),
schema: z.object({ title: z.string() }),
transform: ({ body, metadata, slug }, { collection }) => ({
body: marked.parse(body, { async: false }),
metadata: { ...metadata, url: `/${collection}/${slug}` },
}),
},
},
});- Put derived values inside
metadata.slugandfilecan't change, and any other top-level field is an error. - Return data only: plain objects, arrays, strings, numbers, booleans,
null,Date,Map,Set,URLandRegExp. A function or a class instance is an error that names its key path. transformcan beasync.- In dev, a change reruns
transformonly for the entries whose data changed.
The second argument holds:
collection: the collection's namedev: whether the Vite dev server is runningskip: leaves the document out, see below
Skipping a document
Return skip() to leave a document out of its collection. A reason is optional.
transform: ({ metadata }, { dev, skip }) =>
metadata.draft && !dev ? skip("draft") : {},A skipped document isn't in documents(), slugs() or the generated types. A reference to it fails the build.
Sharing a transform
To use one transform in several collections, write it as a generic function and type its first parameter as Source.
import type { Source, TransformContext } from "tomekit";
function withUrl<TMetadata extends object>(
{ metadata, slug }: Source<TMetadata>,
{ collection }: TransformContext
) {
return { metadata: { ...metadata, url: `/${collection}/${slug}` } };
}Each collection that uses withUrl keeps its own schema's fields, plus url.