Expand description
§rs-web - Static Site Generator
A fast, opinionated static site generator built in Rust with support for:
- Markdown processing with syntax highlighting, external link handling
- Content encryption via Lua (
rs.cryptmodule) - Link graph with backlinks and visualization (Obsidian-style)
- RSS feed generation with section filtering
- Parallel processing for fast builds
- Live reload with automatic browser refresh during watch mode
§Quick Start
# Build the site
rs-web build
# Build to custom output directory
rs-web build --output public
# Watch for changes and rebuild incrementally with live reload
rs-web build --watch
# Watch mode with custom port
rs-web build --watch --port 8080
# Enable debug logging
rs-web --debug build
# Set specific log level (trace, debug, info, warning, error)
rs-web --log-level trace build
# Or use environment variable
RS_WEB_LOG_LEVEL=debug rs-web build§Configuration
Configure via config.lua:
return {
site = {
title = "My Site",
description = "Site description",
base_url = "https://example.com",
author = "Your Name",
},
build = {
output_dir = "dist",
},
-- Generate pages via Lua
pages = function()
return {
{ path = "/", template = "home.html", title = "Home" },
{ path = "/about/", template = "page.html", title = "About" },
}
end,
-- Build hooks
hooks = {
before_build = function() print("Starting...") end,
after_build = function() print("Done!") end,
},
}§Configuration Sections
site- Required: title, description, base_url, authorseo- twitter_handle, default_og_imagebuild- output_dirpaths- styles, static_files, templates
§Lua Sandbox
By default, file operations are sandboxed to the project directory. To disable (use with caution):
return {
lua = { sandbox = false },
site = { ... },
}§Lua API Functions
File Operations:
read_file(path)- Read file contentswrite_file(path, content)- Write content to filecopy_file(src, dest)- Copy file (binary-safe)file_exists(path)- Check if file existslist_files(path, pattern?)- List files matching patternlist_dirs(path)- List subdirectoriesload_json(path)- Load and parse JSON fileload_yaml(path)- Load and parse YAML fileload_toml(path)- Load and parse TOML fileread_frontmatter(path)- Extract frontmatter and content from markdown
Content Processing:
render_markdown(content, transform_fn?)- Convert markdown to HTMLhtml_to_text(html)- Convert HTML to plain textrss_date(date_string)- Format date for RSS (RFC 2822)
Image Processing:
image_dimensions(path)- Get image width and heightimage_resize(input, output, options)- Resize imageimage_convert(input, output, options?)- Convert image formatimage_optimize(input, output, options?)- Optimize/compress image
Asset Building:
build_css(pattern, output, options?)- Build and concatenate CSS files
Text Processing:
slugify(text)- Convert text to URL-friendly slugword_count(text)- Count words in textreading_time(text, wpm?)- Calculate reading time in minutestruncate(text, len, suffix?)- Truncate text with optional suffixstrip_tags(html)- Remove HTML tagsformat_date(date, format)- Format a date stringparse_date(str)- Parse date string to table {year, month, day}hash(content)- Hash content (xxHash64)hash_file(path)- Hash file contentsurl_encode(str)- URL encode a stringurl_decode(str)- URL decode a string
Path Utilities:
join_path(...)- Join path segmentsbasename(path)- Get file name from pathdirname(path)- Get directory from pathextension(path)- Get file extension
Collections:
filter(items, fn)- Filter items where fn returns truesort(items, fn)- Sort items using comparatormap(items, fn)- Transform each itemfind(items, fn)- Find first item where fn returns truegroup_by(items, key_fn)- Group items by keyunique(items)- Remove duplicatesreverse(items)- Reverse array ordertake(items, n)- Take first n itemsskip(items, n)- Skip first n itemskeys(table)- Get all keys from a tablevalues(table)- Get all values from a table
Environment:
env(name)- Get environment variableprint(...)- Log output to build log
Async I/O (rs.async):
fetch(url, options?)- HTTP fetch (blocking)fetch_json(url, options?)- Fetch and parse JSONfetch_all(requests)- Fetch multiple URLs concurrentlyspawn(url, options?)- Spawn async fetch taskawait(task)- Await spawned taskawait_all(tasks)- Await multiple tasksread(path)- Read file as binaryread_file(path)- Read file as textread_files(paths)- Read multiple files concurrentlywrite_file(path, content)- Write filecopy_file(src, dst)- Copy filerename(src, dst)- Rename/move file or directoryremove_file(path)- Remove fileremove_dir(path)- Remove directory recursivelycreate_dir(path)- Create directory (including parents)exists(path)- Check if path existsmetadata(path)- Get file metadataread_dir(path)- List directory contentscanonicalize(path)- Get canonical/absolute path
Encryption (rs.crypt):
encrypt(content, password?)- Encrypt content (AES-256-GCM)decrypt(data, password?)- Decrypt contentencrypt_html(content, options?)- Generate encrypted HTML block for browser decryption
All file operations respect the sandbox setting and are tracked for incremental builds. Encryption uses SITE_PASSWORD environment variable if password is not provided.
§Frontmatter
Post frontmatter options (YAML or TOML):
---
title: "Post Title" # Required
description: "Description" # Optional
date: 2024-01-15 # Optional (YAML date or string)
tags: ["tag1", "tag2"] # Optional
draft: false # Optional (default: false, excluded from build)
image: "/static/post.png" # Optional: OG image
template: "custom.html" # Optional: Override template
slug: "custom-slug" # Optional: Override URL slug
permalink: "/custom/url/" # Optional: Full URL override
---§Encryption (via Lua)
Encryption is handled via the rs.crypt module in Lua. Use SITE_PASSWORD
environment variable or pass password explicitly:
-- Encrypt content
local encrypted = rs.crypt.encrypt("secret content")
-- Returns: { ciphertext, salt, nonce }
-- Generate encrypted HTML block for browser decryption
local html = rs.crypt.encrypt_html("secret content", {
slug = "post-slug",
block_id = "secret-1",
})
-- Decrypt content
local plaintext = rs.crypt.decrypt(encrypted)§Template Variables
§Home Template (home.html)
site- Site config (title, description, base_url, author)page- Page info (title, description, url, image)sections- All sections with posts (sections.blog.posts)content- Rendered markdown content
§Post Template (post.html)
site- Site configpost- Post info (title, url, date, tags, reading_time, etc.)page- Page info for head.html compatibilitycontent- Rendered markdown contentbacklinks- Posts linking to this post (url, title, section)graph- Local graph data (nodes, edges) for visualization
§Graph Template (graph.html)
site- Site configpage- Page infograph- Full graph data (nodes, edges)
§Modules
config- Configuration loading and structuresmarkdown- Markdown processing pipelinetemplates- Tera template renderingencryption- AES-256-GCM encryption utilities (used byrs.cryptLua module)build- Main build orchestrator
Modules§
- assets
- Asset processing (CSS bundling, image optimization)
- build
- Build orchestrator for static site generation
- config
- Configuration loader for rs-web
- data
- Tera template functions for data loading and content rendering
- encryption
- AES-256-GCM encryption with Argon2id key derivation
- git
- Git integration for commit info and file history
- lua
- Lua API functions for rs-web
- markdown
- Markdown processing with syntax highlighting and transformations
- server
- Development server with WebSocket live reload
- templates
- Tera template engine wrapper with page rendering
- text
- Plain text generation from HTML content
- watch
- File watcher for incremental rebuilds