rs_web/
lib.rs

1//! # rs-web - Static Site Generator
2//!
3//! A fast, opinionated static site generator built in Rust with support for:
4//!
5//! - **Markdown processing** with syntax highlighting, external link handling
6//! - **Content encryption** via Lua (`rs.crypt` module)
7//! - **Link graph** with backlinks and visualization (Obsidian-style)
8//! - **RSS feed** generation with section filtering
9//! - **Parallel processing** for fast builds
10//! - **Live reload** with automatic browser refresh during watch mode
11//! - **Asset minification** for CSS, JS (with dead code elimination), and HTML
12//!
13//! ## Quick Start
14//!
15//! ```bash
16//! # Build the site
17//! rs-web build
18//!
19//! # Build to custom output directory
20//! rs-web build --output public
21//!
22//! # Watch for changes and rebuild incrementally with live reload
23//! rs-web build --watch
24//!
25//! # Watch mode with custom port
26//! rs-web build --watch --port 8080
27//!
28//! # Enable debug logging
29//! rs-web --debug build
30//!
31//! # Set specific log level (trace, debug, info, warning, error)
32//! rs-web --log-level trace build
33//!
34//! # Or use environment variable
35//! RS_WEB_LOG_LEVEL=debug rs-web build
36//! ```
37//!
38//! ## Configuration
39//!
40//! Configure via `config.lua`:
41//!
42//! ```lua
43//! return {
44//!   site = {
45//!     title = "My Site",
46//!     description = "Site description",
47//!     base_url = "https://example.com",
48//!     author = "Your Name",
49//!   },
50//!
51//!   build = {
52//!     output_dir = "dist",
53//!   },
54//!
55//!   -- Generate pages via Lua
56//!   pages = function()
57//!     return {
58//!       { path = "/", template = "home.html", title = "Home" },
59//!       { path = "/about/", template = "page.html", title = "About", minify = true },
60//!     }
61//!   end,
62//!
63//!   -- Build hooks
64//!   hooks = {
65//!     before_build = function() print("Starting...") end,
66//!     after_build = function() print("Done!") end,
67//!   },
68//! }
69//! ```
70//!
71//! ### Configuration Sections
72//!
73//! - `site` - Required: title, description, base_url, author
74//! - `seo` - twitter_handle, default_og_image
75//! - `build` - output_dir
76//! - `paths` - templates
77//!
78//! ### Lua Sandbox
79//!
80//! By default, file operations are sandboxed to the project directory.
81//! To disable (use with caution):
82//!
83//! ```lua
84//! return {
85//!   lua = { sandbox = false },
86//!   site = { ... },
87//! }
88//! ```
89//!
90//! ### Lua API Functions
91//!
92//! **File Operations:**
93//! - `read_file(path)` - Read file contents
94//! - `write_file(path, content)` - Write content to file
95//! - `copy_file(src, dest)` - Copy file (binary-safe)
96//! - `file_exists(path)` - Check if file exists
97//! - `list_files(path, pattern?)` - List files matching pattern
98//! - `list_dirs(path)` - List subdirectories
99//! - `load_json(path)` - Load and parse JSON file
100//! - `load_yaml(path)` - Load and parse YAML file
101//! - `load_toml(path)` - Load and parse TOML file
102//! - `read_frontmatter(path)` - Extract frontmatter and content from markdown
103//!
104//! **Content Processing:**
105//! - `html_to_text(html)` - Convert HTML to plain text
106//! - `rss_date(date_string)` - Format date for RSS (RFC 2822)
107//!
108//! **Markdown (rs.markdown):**
109//! - `render(content, opts?)` - Render markdown to HTML with optional plugins
110//! - `plugins(...)` - Combine/flatten plugins
111//! - `plugins.default(opts?)` - Get default plugins (lazy_images, heading_anchors, external_links)
112//! - `plugins.lazy_images(opts?)` - Plugin: add loading="lazy" decoding="async" to images
113//! - `plugins.heading_anchors(opts?)` - Plugin: add id="slug" to headings
114//! - `plugins.external_links(opts?)` - Plugin: add target="_blank" rel="noopener" to external links
115//!
116//! **Image Processing:**
117//! - `image_dimensions(path)` - Get image width and height
118//! - `image_resize(input, output, options)` - Resize image
119//! - `image_convert(input, output, options?)` - Convert image format
120//! - `image_optimize(input, output, options?)` - Optimize/compress image
121//!
122//! **Fonts (rs.fonts):**
123//! - `download_google_font(family, options)` - Download Google Font with optional CSS minification (async)
124//!
125//! **JS Module (rs.js):**
126//! - `concat(paths, output, options?)` - Concatenate JS files with optional minification (async)
127//! - `bundle(entry, output, options?)` - Bundle JS with imports via Rolldown (async)
128//! - `bundle_many(entries, output_dir, options?)` - Bundle multiple JS entries to separate files (async)
129//!
130//! **CSS Module (rs.css):**
131//! - `concat(paths, output, options?)` - Concatenate CSS files with optional minification (async)
132//! - `bundle(paths, output, options?)` - Bundle CSS with @import resolution via LightningCSS (async)
133//! - `bundle_many(paths, output_dir, options?)` - Bundle multiple CSS entries to separate files (async)
134//! - `purge(css_path, options?)` - Remove unused CSS rules based on HTML/JS output (async, call in after_build)
135//! - `critical(html, css_path, options?)` - Extract critical CSS for a specific HTML page (async)
136//! - `inline_critical(html_path, css_path, options?)` - Inline critical CSS into HTML with async loading (async)
137//!
138//! **Asset Hashing (rs.assets):**
139//! - `hash(content, length?)` - Compute SHA256 hash of content (async)
140//! - `hash_sync(content, length?)` - Compute hash synchronously
141//! - `write_hashed(content, path, options?)` - Write file with hashed filename (async)
142//! - `register(original, hashed)` - Register asset path mapping
143//! - `get_path(path)` - Get hashed path for original
144//! - `manifest()` - Get all path mappings
145//! - `clear()` - Clear the manifest
146//!
147//! **PWA (rs.pwa):**
148//! - `manifest(options)` - Generate web app manifest.json (async)
149//! - `service_worker(options)` - Generate service worker sw.js (async)
150//!
151//! **SEO (rs.seo):**
152//! - `sitemap(options)` - Generate XML sitemap (async)
153//! - `robots(options)` - Generate robots.txt (async)
154//!
155//! **Text Processing:**
156//! - `slugify(text)` - Convert text to URL-friendly slug
157//! - `word_count(text)` - Count words in text
158//! - `reading_time(text, wpm?)` - Calculate reading time in minutes
159//! - `truncate(text, len, suffix?)` - Truncate text with optional suffix
160//! - `strip_tags(html)` - Remove HTML tags
161//! - `format_date(date, format)` - Format a date string
162//! - `parse_date(str)` - Parse date string to table {year, month, day}
163//! - `hash(content)` - Hash content (xxHash64)
164//! - `hash_file(path)` - Hash file contents
165//! - `url_encode(str)` - URL encode a string
166//! - `url_decode(str)` - URL decode a string
167//!
168//! **Path Utilities:**
169//! - `join_path(...)` - Join path segments
170//! - `basename(path)` - Get file name from path
171//! - `dirname(path)` - Get directory from path
172//! - `extension(path)` - Get file extension
173//!
174//! **Collections:**
175//! - `filter(items, fn)` - Filter items where fn returns true
176//! - `sort(items, fn)` - Sort items using comparator
177//! - `map(items, fn)` - Transform each item
178//! - `find(items, fn)` - Find first item where fn returns true
179//! - `group_by(items, key_fn)` - Group items by key
180//! - `unique(items)` - Remove duplicates
181//! - `reverse(items)` - Reverse array order
182//! - `take(items, n)` - Take first n items
183//! - `skip(items, n)` - Skip first n items
184//! - `keys(table)` - Get all keys from a table
185//! - `values(table)` - Get all values from a table
186//!
187//! **Environment:**
188//! - `env(name, default?)` - Get environment variable with optional default
189//! - `print(...)` - Log output to build log
190//! - `git_info(path?)` - Get git info (hash, branch, author, date, dirty)
191//!
192//! **Parallel (rs.parallel):** Rayon-backed true parallel operations
193//! - `load_json(paths)` / `load_yaml(paths)` - Load multiple files in parallel
194//! - `read_files(paths)` / `read_frontmatter(paths)` - Read multiple files
195//! - `create_dirs(paths)` / `copy_files(sources, dests)` - Parallel file operations
196//! - `image_convert(sources, dests, opts?)` - Convert images in parallel
197//! - `map(items, fn, ctx?)` / `filter(items, fn, ctx?)` - Parallel map/filter with explicit context
198//! - `map_seq(items, fn)` / `filter_seq(items, fn)` - Sequential fallbacks for non-serializable items
199//!
200//! **Async I/O (rs.async):** All return handles, await with `rs.async.await(task)` or `rs.async.await_all(tasks)`
201//! - `fetch(url, opts?)` / `fetch_bytes(url, opts?)` - HTTP fetch (text/binary)
202//! - `fetch_sync(url, opts?)` - Blocking fetch (returns response directly)
203//! - `write_file(path, content)` / `write(path, bytes)` - Write text/binary
204//! - `copy_file(src, dst)` / `create_dir(path)` - File/dir operations
205//! - `exists(path)` / `read_file(path)` - Check existence / read file
206//!
207//! **Encryption (rs.crypt):**
208//! - `encrypt(content, password?)` - Encrypt content (AES-256-GCM)
209//! - `decrypt(data, password?)` - Decrypt content
210//! - `encrypt_html(content, options?)` - Generate encrypted HTML block for browser decryption
211//!
212//! All file operations respect the sandbox setting and are tracked for incremental builds.
213//! Encryption uses SITE_PASSWORD environment variable if password is not provided.
214//!
215//! ## Frontmatter
216//!
217//! Post frontmatter options (YAML or TOML):
218//!
219//! ```yaml
220//! ---
221//! title: "Post Title"           # Required
222//! description: "Description"    # Optional
223//! date: 2024-01-15              # Optional (YAML date or string)
224//! tags: ["tag1", "tag2"]        # Optional
225//! draft: false                  # Optional (default: false, excluded from build)
226//! image: "/static/post.png"     # Optional: OG image
227//! template: "custom.html"       # Optional: Override template
228//! slug: "custom-slug"           # Optional: Override URL slug
229//! permalink: "/custom/url/"     # Optional: Full URL override
230//! ---
231//! ```
232//!
233//! ## Encryption
234//!
235//! Encryption is handled via the `rs.crypt` module in Lua. Use `SITE_PASSWORD`
236//! environment variable or pass password explicitly:
237//!
238//! ```lua
239//! -- Encrypt content
240//! local encrypted = rs.crypt.encrypt("secret content")
241//! -- Returns: { ciphertext, salt, nonce }
242//!
243//! -- Generate encrypted HTML block for browser decryption
244//! local html = rs.crypt.encrypt_html("secret content", {
245//!   slug = "post-slug",
246//!   block_id = "secret-1",
247//! })
248//!
249//! -- Decrypt content
250//! local plaintext = rs.crypt.decrypt(encrypted)
251//! ```
252//!
253//! ## Template Variables
254//!
255//! ### Home Template (`home.html`)
256//! - `site` - Site config (title, description, base_url, author)
257//! - `page` - Page info (title, description, url, image)
258//! - `sections` - All sections with posts (`sections.blog.posts`)
259//! - `content` - Rendered markdown content
260//!
261//! ### Post Template (`post.html`)
262//! - `site` - Site config
263//! - `post` - Post info (title, url, date, tags, reading_time, etc.)
264//! - `page` - Page info for head.html compatibility
265//! - `content` - Rendered markdown content
266//! - `backlinks` - Posts linking to this post (url, title, section)
267//! - `graph` - Local graph data (nodes, edges) for visualization
268//!
269//! ### Graph Template (`graph.html`)
270//! - `site` - Site config
271//! - `page` - Page info
272//! - `graph` - Full graph data (nodes, edges)
273//!
274//! ## Modules
275//!
276//! - [`config`] - Configuration loading and structures
277//! - [`lua`] - Lua API including `rs.markdown` for markdown processing
278//! - [`templates`] - Tera template rendering
279//! - [`encryption`] - AES-256-GCM encryption utilities (used by `rs.crypt` Lua module)
280//! - [`build`] - Main build orchestrator
281
282/// Print output at info level (always shown regardless of log level)
283#[macro_export]
284macro_rules! rs_print {
285    ($($arg:tt)*) => {
286        log::info!(target: "rs_print", "{}", format!($($arg)*))
287    };
288}
289
290pub mod assets;
291pub mod build;
292pub mod config;
293pub mod data;
294pub mod encryption;
295pub mod git;
296pub mod lua;
297pub mod server;
298pub mod templates;
299pub mod text;
300pub mod tracker;
301pub mod watch;