@williamcachamwri/markui-compiler
v0.2.0
Published
Markdown and directive compiler for MarkUI
Maintainers
Readme
@williamcachamwri/markui-compiler
Compiles Markdown, YAML frontmatter, GFM, and strict Markdown directives into the versioned, framework-neutral MarkUI AST.
Install
npm install @williamcachamwri/markui-compilerCompile Markdown
import { compileMarkdown } from '@williamcachamwri/markui-compiler'
const result = compileMarkdown(`
---
title: Account
---
# {{ state.user.name }}
::input{name="email" bind="state.user.email"}
::::card{title="Actions"}
:::button{action="save"}
Save
:::
::::
`, {
filename: 'src/pages/account.md',
})
if (!result.valid) {
console.error(result.diagnostics)
}Use ::name{...} for a leaf directive. Use matching fences of at least three colons for a directive containing Markdown children.
:::card{title="Profile"}
Content
:::Nested containers require longer outer fences:
::::grid
:::card
Content
:::
::::This rule matches the directive parser and removes ambiguity about which level a closing marker belongs to.
Strict validation
compileMarkdown performs a source-positioned directive scan before remark-directive creates the Markdown AST. A document with directive syntax errors is invalid and receives an empty renderable child list, preventing malformed marker text from reaching a renderer.
The validator ignores directive-looking content inside fenced code blocks, inline code, escaped text, and ordinary prose where the marker is not in directive position.
Stable directive diagnostic codes:
| Code | Meaning |
|---|---|
| MUI1001 | Malformed directive statement |
| MUI1002 | Unclosed container directive |
| MUI1003 | Unexpected closing marker |
| MUI1004 | Invalid container nesting or fence width |
| MUI1005 | Leaf-only component used as a container |
| MUI1006 | Reserved for container-only syntax validation |
| MUI1007 | Unknown directive/component |
| MUI1008 | Invalid or duplicate attributes |
| MUI1009 | Trailing directive marker |
| MUI1010 | Ambiguous syntax or leaked marker invariant |
Example failure:
:::card
Missing closesrc/pages/account.md:1:1 [MUI1002]
Container directive "card" is not closed.Structured diagnostics
const result = compileMarkdown(source, {
filename: 'src/pages/account.md',
})
for (const diagnostic of result.diagnostics) {
console.log({
code: diagnostic.code,
severity: diagnostic.severity,
file: diagnostic.file,
line: diagnostic.line,
column: diagnostic.column,
endLine: diagnostic.endLine,
endColumn: diagnostic.endColumn,
message: diagnostic.message,
hint: diagnostic.hint,
related: diagnostic.related,
})
}Set throwOnError: true to throw MarkUICompileError containing the same diagnostics.
Options
interface CompileOptions {
filename?: string
registry?: ComponentRegistry
allowHtml?: boolean
throwOnError?: boolean
}filenameis included in diagnostics and the generated document.registrycontrols which components, syntax forms, and props are valid.allowHtmlpreserves raw HTML nodes. It does not sanitize them.throwOnErrorthrows when an error diagnostic exists.
Component schema validation
The compiler combines grammar validation with the component registry. It checks:
- registered component names;
allowChildrenand leaf/container syntax;- required props;
- static prop types and enum values;
- duplicate directive attributes;
- unknown props, which currently produce warnings;
- expression and template syntax.
A component with allowChildren: false, such as stat, cannot use container syntax:
:::stat{value="10"}
Invalid child content
:::Use the leaf form instead:
::stat{value="10"}URL policy
Link and image URLs allow:
- relative and fragment URLs;
http:andhttps:;mailto:andtel:.
Other explicit protocols, including javascript:, data:, and vbscript:, produce an error and compile to an empty URL. Control characters and whitespace cannot disguise a blocked protocol.
Raw HTML
Raw HTML is disabled by default and emitted as text with a warning. Setting allowHtml: true preserves a raw-html AST node:
const result = compileMarkdown('<b>trusted source</b>', {
allowHtml: true,
})The renderer must still require an explicit sanitizer. Enabling this option is not a substitute for sanitization.
Frontmatter logic
The compiler treats frontmatter as data. Resolution of a logic module is performed by @williamcachamwri/markui-vite, which only accepts relative module paths beginning with ./ or ../.
Compatibility
Generated documents include the current MARKUI_DOCUMENT_VERSION. Consumers should validate that version through @williamcachamwri/markui-core before rendering or transforming the AST.
