muddle/views/engine/types.ts
2026-10-23 08:53:07 -04:00

244 lines
4.6 KiB
TypeScript

export type TokenType =
| "tag"
| "text"
| "code"
| "out"
| "comment"
| "doctype"
| "keyword"
| "frontmatter"
| "test"
| "rawtext";
export interface RawAttribute {
name: string;
value?: string;
kind: "string" | "expression" | "boolean";
}
/** A single line of a template, already split into its semantic parts. */
export interface Token {
type: TokenType;
/** Indentation width, used to build the tree. */
indent: number;
/** Line number in the source file, used for error messages. */
line: number;
/** Raw text of the line. */
value: string;
/** Tag name, only for `tag` tokens. */
name?: string;
/** Attributes, only for `tag` tokens. */
attributes?: RawAttribute[];
/** Inline text following the tag, only for `tag` tokens. */
text?: string;
/** Buffered expression of the tag, e.g. `p= title`. */
output?: string;
/** Keyword name, only for `keyword` tokens. */
keyword?: string;
/** Keyword payload (`each item in items`), only for `keyword` tokens. */
keywordValue?: string;
/** Whether the tag is followed by a block of raw text (`p.`). */
blockText?: boolean;
/** A generated relation identity declared with `tag@name()` in SPUG. */
anchor?: { name: string; expression?: string };
/** Whether a comment is silent (`//-`). */
silent?: boolean;
/** Whether an output expression is escaped (`=` vs `!=`). */
escaped?: boolean;
}
export type TextPart =
| { type: "literal"; value: string }
| { type: "expression"; code: string; escaped: boolean };
export interface PropDeclaration {
name: string;
/** Type expression as written in the template. */
type: string;
optional: boolean;
/** Default value expression, when declared with `=`. */
defaultValue?: string;
}
export interface ElementNode {
kind: "element";
name: string;
attributes: RawAttribute[];
children: Node[];
selfClosing: boolean;
/** A generated relation identity declared with `tag@name()` in SPUG. */
anchor?: { name: string; expression?: string };
}
export interface TextNode {
kind: "text";
parts: TextPart[];
}
export interface RawTextNode {
kind: "rawtext";
value: string;
}
export interface CodeNode {
kind: "code";
code: string;
}
export interface OutNode {
kind: "out";
code: string;
escaped: boolean;
}
export interface CommentNode {
kind: "comment";
value: string;
silent: boolean;
children: Node[];
}
export interface DoctypeNode {
kind: "doctype";
value: string;
}
export interface IfBranch {
/** `null` marks the `else` branch. */
test: string | null;
negate: boolean;
children: Node[];
}
export interface IfNode {
kind: "if";
branches: IfBranch[];
}
export interface EachNode {
kind: "each";
value: string;
key?: string;
list: string;
children: Node[];
}
export interface WhileNode {
kind: "while";
test: string;
children: Node[];
}
export interface CaseNode {
kind: "case";
subject: string;
cases: { test?: string; children: Node[] }[];
}
export interface MixinDefNode {
kind: "mixin";
name: string;
params: string;
children: Node[];
}
export interface MixinCallNode {
kind: "mixin-call";
name: string;
args: string;
children: Node[];
}
export interface IncludeNode {
kind: "include";
path: string;
/** Included file content, resolved by the linker. */
children: Node[];
}
export interface ExtendsNode {
kind: "extends";
path: string;
}
export interface BlockNode {
kind: "block";
name: string;
mode: "replace" | "append" | "prepend";
children: Node[];
}
export interface SlotNode {
kind: "slot";
name: string;
children: Node[];
}
export interface YieldNode {
kind: "yield";
name: string;
}
export interface PropsNode {
kind: "props";
props: PropDeclaration[];
}
export interface ComponentNode {
kind: "component" | "island";
name: string;
/** Prop name to expression. */
props: Record<string, string>;
/** Renderers that are applied inline by the component. */
renders: { name: string; children: Node[] }[];
}
/**
* A block of assertions.
*/
export interface TestNode {
kind: "test";
/** The assertions of the block. */
code: string;
}
/**
* A renderer that is applied inline.
*/
export interface RenderNode {
kind: "render";
/** Name of the prop the renderer is handed to. */
name: string;
children: Node[];
}
export interface PortalNode {
kind: "portal";
name: string;
children: Node[];
}
export type Node =
| ElementNode
| TextNode
| RawTextNode
| CodeNode
| OutNode
| CommentNode
| DoctypeNode
| IfNode
| EachNode
| WhileNode
| CaseNode
| MixinDefNode
| MixinCallNode
| IncludeNode
| ExtendsNode
| BlockNode
| SlotNode
| YieldNode
| PropsNode
| ComponentNode
| TestNode
| RenderNode
| PortalNode;