Description
While build-time pre-parsers like comark-knap use a separate engine to process knap syntax before comark parses the markdown, @comark/binding handles templating natively at the AST level during runtime.
With loops (#411) and conditionals (#415) already implemented natively in @comark/binding, the remaining key opportunity is bringing knap's chained pipe filters (e.g., {{ text | upper }}) directly into @comark/binding. Note that this does not conflict with the existing default value operator ||.
Motivation
Developers wanting to use knap-style templating currently rely on third-party pre-processors like comark-knap. While comark-knap compiles templates at build time into raw Markdown, running a separate pre-parser bypasses comark's AST lifecycle.
There is an opportunity to bring core data-transformation features natively into @comark/binding at the AST level while keeping structural rendering cleanly delegated to Comark components.
Comparison Matrix
| Feature |
knap / comark-knap (Third-party Pre-parser) |
Native @comark/binding (First-party Runtime) |
Status in @comark/binding |
| Execution Context |
Pre-parser / String replace |
AST / Runtime evaluator |
✅ Built-in |
Loops ({% for %}) |
Supported |
Supported natively |
✅ Implemented (#411) |
Conditionals ({% if %}) |
Supported |
Supported natively |
✅ Implemented (#415) |
| Chained Pipe Filters |
Supported ({{ value | upper }}) |
Not Supported |
🎯 High Priority |
| Markdown Generators |
Supported ({{ data | table }}) |
Won't Fix / Out of Scope |
❌ Delegated to Components |
| Inline Variable Assignment |
Supported ({% set x = 1 %}) |
Not Supported |
❌ Low Priority |
Feature Evaluation
- Chained Pipe Filters (
|) — High Priority: Add support for readable pipeline transformations (e.g., {{ title | upper | truncate:10 }}) via a native filters configuration option in @comark/binding. Pipe filters operate strictly on data values, transforming primitives/objects before they are rendered into attributes or text nodes.
- Markdown Generators — Out of Scope: Generating complex structural AST nodes (like
TableNode or ListNode) directly from inside inline text expressions conflates value evaluation with AST layout rendering. In Comark's design model, structural rendering belongs in dedicated components (e.g., <Table :data="data"/>), not inline template filters.
- Inline Variable Assignment (
{% set %}) — Low Priority: Unnecessary for AST-level runtime evaluation. Scope variables should be passed directly via context or handled via filters rather than introducing state mutability inside AST expressions.
Proposed solution
To support functional transformations cleanly within the AST lifecycle, @comark/binding will introduce a configurable Pipe Filter Pipeline (|).
Filters will execute purely as value-to-value transformations during expression evaluation, leaving AST structure and layout responsibilities to standard Comark components.
1. Configuration & Registration
Add a filters option to the plugin configuration, allowing developers to define global or contextual transformation functions:
import { comarkBinding } from '@comark/binding'
const bindingPlugin = comarkBinding({
filters: {
upper: (val: string) => val?.toUpperCase(),
truncate: (val: string, length: number) =>
val?.length > length ? `${val.slice(0, length)}...` : val,
json: (val: unknown, indent = 2) => JSON.stringify(val, null, indent),
date: (val: string | Date, formatStr: string) => formatDate(val, formatStr)
}
})
2. Pipeline Expression Syntax
Update the expression parser to handle standard Unix-style pipe operators (|) with optional arguments:
<!-- Basic transformation -->
# {{ title | upper }}
<!-- Chained filters with arguments -->
<p>{{ article.summary | truncate:120 | upper }}</p>
<!-- Attribute binding support -->
<UserCard :created-at="user.createdAt | date:'YYYY-MM-DD'" :name="user.name | upper"/>
3. Execution Lifecycle
- AST Node Identification: The parser identifies interpolation nodes (text or attribute bindings).
- Value Evaluation:
@comark/binding resolves the base identifier path against the execution context.
- Pipeline Sequential Pass: The output of the base expression is passed sequentially as the first parameter into each chained filter function, with explicit arguments passed subsequently.
- AST Rendering: The transformed scalar value or object is injected back into the AST node text or prop payload.
4. Handling Structural Layout (Generators Alternative)
Rather than embedding layout logic inside filters, complex structures continue to leverage comark’s native component bindings:
<!-- Preferred architectural pattern over {{ data | table }} -->
<Table :data="data | sort:'name'"/>
Additional context
Description
While build-time pre-parsers like
comark-knapuse a separate engine to processknapsyntax beforecomarkparses the markdown,@comark/bindinghandles templating natively at the AST level during runtime.With loops (#411) and conditionals (#415) already implemented natively in
@comark/binding, the remaining key opportunity is bringing knap's chained pipe filters (e.g.,{{ text | upper }}) directly into@comark/binding. Note that this does not conflict with the existing default value operator||.Motivation
Developers wanting to use
knap-style templating currently rely on third-party pre-processors likecomark-knap. Whilecomark-knapcompiles templates at build time into raw Markdown, running a separate pre-parser bypasses comark's AST lifecycle.There is an opportunity to bring core data-transformation features natively into
@comark/bindingat the AST level while keeping structural rendering cleanly delegated to Comark components.Comparison Matrix
knap/comark-knap(Third-party Pre-parser)@comark/binding(First-party Runtime)@comark/binding{% for %}){% if %}){{ value | upper }}){{ data | table }}){% set x = 1 %})Feature Evaluation
|) — High Priority: Add support for readable pipeline transformations (e.g.,{{ title | upper | truncate:10 }}) via a nativefiltersconfiguration option in@comark/binding. Pipe filters operate strictly on data values, transforming primitives/objects before they are rendered into attributes or text nodes.TableNodeorListNode) directly from inside inline text expressions conflates value evaluation with AST layout rendering. In Comark's design model, structural rendering belongs in dedicated components (e.g.,<Table :data="data"/>), not inline template filters.{% set %}) — Low Priority: Unnecessary for AST-level runtime evaluation. Scope variables should be passed directly via context or handled via filters rather than introducing state mutability inside AST expressions.Proposed solution
To support functional transformations cleanly within the AST lifecycle,
@comark/bindingwill introduce a configurable Pipe Filter Pipeline (|).Filters will execute purely as value-to-value transformations during expression evaluation, leaving AST structure and layout responsibilities to standard Comark components.
1. Configuration & Registration
Add a
filtersoption to the plugin configuration, allowing developers to define global or contextual transformation functions:2. Pipeline Expression Syntax
Update the expression parser to handle standard Unix-style pipe operators (
|) with optional arguments:3. Execution Lifecycle
@comark/bindingresolves the base identifier path against the execution context.4. Handling Structural Layout (Generators Alternative)
Rather than embedding layout logic inside filters, complex structures continue to leverage comark’s native component bindings:
Additional context
comark-knapGitHub Repository – Third-party build-time pre-parser plugin by Atinux.comark-knapLive Demo – Interactive playground demonstrating Knap rendering in Comark.{% for %}) in@comark/binding.{% if %}) in@comark/binding.