8 releases
Uses new Rust 2024
| 0.2.0 | Jan 17, 2026 |
|---|---|
| 0.1.6 | Dec 12, 2025 |
| 0.1.5 | Nov 12, 2025 |
#192 in Development tools
170KB
2K
SLoC
liaison
A content transclusion tool that materializes references into source files in place, perfect for documentation that embeds code snippets.
Works with HTML, Markdown, Rust, TypeScript, Python, and any text format.
Installation
cargo install liaison
Or from source:
git clone https://github.com/dnorman/liaison
cd liaison
cargo install --path .
Quick Start
1. Mark content in your source files:
// src/lib.rs
pub fn example() {
// liaison id=demo-code
let x = 42;
println!("Hello: {}", x);
// liaison end
}
2. Reference it in documentation:
<!-- README.md -->
Here's how to use it:
<!-- liaison transclude="src/lib.rs#demo-code" -->
<!-- liaison end -->
3. Run liaison:
liaison README.md
4. Your documentation now contains the actual code:
<!-- README.md -->
Here's how to use it:
<!-- liaison transclude="src/lib.rs#demo-code" -->
let x = 42;
println!("Hello: {}", x);
<!-- liaison end -->
Usage
# Process specific files
liaison index.html README.md
# Check what would change (dry run)
liaison --check README.md
# Clear all transcluded content (for testing)
liaison --reset README.md
# Continue processing even if some transclusions fail
liaison --ignore-errors README.md
# Process files matching patterns (.liaison.toml)
liaison
Configuration
Create .liaison.toml in your repository root:
[glob]
include = ["docs/**/*.{md,html}", "README.md"]
exclude = ["target/**", "node_modules/**"]
Default: Empty include list (process nothing unless files specified via CLI).
Syntax
Plaintext Files
For code files (.rs, .ts, .py, .js, etc.) and Markdown:
Define reusable blocks:
// liaison id=my-function
fn my_function() -> i32 {
42
}
// liaison end
Reference them:
<!-- liaison transclude="src/lib.rs#my-function" -->
<!-- liaison end -->
Comment styles auto-detected:
- Rust, TypeScript, JavaScript:
// - Python, Shell:
# - Markdown, HTML:
<!-- -->
HTML Files
HTML supports two transclusion styles:
Element transclusion (using attributes):
<!-- source.html -->
<section id="intro">
<h1>Welcome</h1>
<p>Getting started guide</p>
</section>
<!-- target.html -->
<div transclude="source.html#intro"></div>
After running liaison:
<div transclude="source.html#intro">
<h1>Welcome</h1>
<p>Getting started guide</p>
</div>
Comment transclusion (same syntax as Markdown):
<!-- works in HTML files too -->
<!-- liaison transclude="components/header.html#banner" -->
<!-- liaison end -->
Self-closing tags are automatically expanded:
<!-- before -->
<header transclude="header.html#nav" />
<!-- after -->
<header transclude="header.html#nav">
<nav>...</nav>
</header>
Features
Whitespace Normalization
Content is automatically dedented based on the marker's indentation:
fn main() {
// liaison id=indented
let x = 5;
if x > 0 {
println!("positive");
}
// liaison end
}
Extracts as:
let x = 5;
if x > 0 {
println!("positive");
}
Recursive Transclusion
Transcluded content can itself contain transclusions, which are automatically expanded:
<!-- index.html transcludes header.html -->
<header transclude="header.html#banner"></header>
<!-- header.html contains a nested transclude -->
<div id="banner">
<h1>My Site</h1>
<!-- liaison transclude="tagline.md#slogan" -->
<!-- liaison end -->
</div>
All levels are expanded in a single pass.
HTML Indentation
HTML hosts automatically indent transcluded content to match the target element:
<div transclude="content.html#section">
<!-- content is indented to match -->
</div>
Cycle Detection
Prevents infinite loops from circular references.
Atomic Operations
All changes succeed or none are applied—no partial updates on errors.
Remote Content
Fetch content from HTTP(S) URLs:
<!-- liaison transclude="https://example.com/api/snippet.rs#demo" -->
<!-- liaison end -->
HTML Escaping
Code from plaintext files is automatically HTML-escaped when transcluded into HTML:
<pre><code transclude="src/lib.rs#generic-function"></code></pre>
Rust code with <T> generics becomes <T> in HTML.
Path Resolution
Paths are resolved relative to the Git repository root of the file being processed:
# Works from any directory
cd ~/projects/tool && liaison ~/projects/docs/index.html
File-relative paths (since v0.1.0): Paths are first tried relative to the current file's directory, then fall back to repo-relative:
<!-- docs/index.html -->
<div transclude="header.html#banner"></div>
<!-- Looks in docs/header.html first -->
Cross-repository: All files in a single command must be from the same repository.
Safety
- No directory traversal:
..in paths is rejected - Git-aware: Automatically detects repository boundaries
- Atomic writes: Changes are transactional
- Preserves structure: Only innerHTML is replaced, attributes preserved
CLI Reference
liaison [OPTIONS] [PATH]...
Arguments:
[PATH]... Files to process (overrides glob config)
Options:
--check Check if changes would be made (dry run)
--reset Clear all transcluded content to empty
--ignore-errors Continue processing even if some transclusions fail
-h, --help Print help
-V, --version Print version
Examples
See the demo/ directory and tests/fixtures/ for working examples.
Use Cases
- Documentation: Keep code examples in sync with actual source
- Static sites: Embed code snippets from your repository
- Books/tutorials: Auto-update code blocks from tested examples
- API docs: Include implementation snippets inline
The Maniacal Plan
Transclusion—a term coined by Ted Nelson in 1963—is the inclusion of one document inside another by reference. Instead of copying content, you point to it. The source stays authoritative; the reference stays current.
Most formats don't support this natively. Liaison fixes that.
Phase 1 (you are here)
CLI transclusion between HTML and plaintext files. Define blocks with id, reference them with transclude, run the command. Useful, harmless, nothing to see here.
Phase 2
Web content. Fetch from remote URLs with caching and authentication. Full CSS selector support for extracting content from any webpage. Now we can point at anything on the internet.
Phase 3
More formats. XML (XPath), JSON (JSONPath), CSV (cell ranges), spreadsheets, maybe even Word docs. If it can be parsed, it can be transcluded.
Phase 4
A daemon. Watches your files and folders for changes. Re-materializes references automatically. Support for transcluding entire files into directories. Keeps everything synchronized. While you sleep.
Phase 5
Graph database overlay. The transclusion graph becomes its own persistent layer—point to things that live only in the graph, or pull external content into the graph by pointing at it. Track the live version, the archived version, or both. The graph remembers everything you've ever referenced.
Phase 6
At this point, files are just... views. The graph is the source of truth. Liaison is the filesystem muahahaha.
License
Dual-licensed under MIT or Apache 2.0 (your choice).
Dependencies
~13–30MB
~434K SLoC