HFU Digital Docs
LoopKitBackend

Content Pipeline

Template rendering with optional Markdown, KaTeX, and code highlighting transforms

Overview

The content pipeline handles rendering note fields into card HTML. It consists of two stages:

  1. Template interpolation — Replace {{FieldName}} placeholders with field values
  2. Content transforms — Apply optional Markdown, math, code highlighting, and sanitization

Template Syntax

SyntaxDescription
{{FieldName}}Replace with the field's value
{{#FieldName}}...{{/FieldName}}Show content only if field is non-empty
{{^FieldName}}...{{/FieldName}}Show content only if field is empty
{{FrontSide}}Insert the rendered front (back template only)

Example template:

{{Front}}

{{#Extra}}
<hr>
<small>{{Extra}}</small>
{{/Extra}}

Creating a Pipeline

import {
    createContentPipeline,
    createMarkdownTransform,
    createKatexTransform,
    createCodeHighlightTransform,
    createSanitizeTransform,
} from '@hfu.digital/loopkit-nestjs';

Basic Pipeline (No Transforms)

const pipeline = createContentPipeline();
// Only template interpolation — no Markdown, math, etc.

Full Pipeline

import { parse } from 'marked';
import katex from 'katex';
import hljs from 'highlight.js';
import sanitize from 'sanitize-html';

const pipeline = createContentPipeline([
    createMarkdownTransform({ parse }),
    createKatexTransform(katex),
    createCodeHighlightTransform(hljs),
    createSanitizeTransform(sanitize),   // Always last!
]);

Built-in Transforms

createMarkdownTransform

function createMarkdownTransform(marked: {
    parse: (src: string) => string;
}): ContentTransform

Converts Markdown to HTML. Requires marked as a peer dependency.

createKatexTransform

function createKatexTransform(katex: {
    renderToString: (tex: string, options?: Record<string, unknown>) => string;
}): ContentTransform

Renders math expressions:

  • Block math: $$E = mc^2$$ — rendered with displayMode: true
  • Inline math: $x^2$ — rendered with displayMode: false

createCodeHighlightTransform

function createCodeHighlightTransform(hljs: {
    highlightAuto: (code: string) => { value: string };
}): ContentTransform

Highlights code blocks using highlight.js auto-detection.

createSanitizeTransform

function createSanitizeTransform(
    sanitize: (dirty: string) => string,
): ContentTransform

Sanitizes HTML to prevent XSS. Should always be the last transform in the pipeline.

ContentTransform Type

A transform is a function that receives HTML and an optional render context:

type ContentTransform = (html: string, context: RenderContext) => string;

interface RenderContext {
    fields: Record<string, string>;  // Note field values by name
    cardState: CardState;            // Current card state
}

See Rendering Types for full details.

ContentPipeline Interface

interface ContentPipeline {
    transforms: ContentTransform[];
    render(note: NoteBase, template: TemplateDef): RenderedCard;
}

The render method:

  1. Builds a field map from the note's fields
  2. Interpolates the front template
  3. Applies all transforms to the front
  4. Interpolates the back template (with {{FrontSide}} available)
  5. Applies all transforms to the back
  6. Returns { front: string, back: string }

Template Interpolation

function interpolateTemplate(
    template: string,
    fields: Record<string, string>,
    frontRendered?: string,
): string

This function is also exported if you need standalone template processing.

On this page