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; /** 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;