The bash::framehead wiki generator uses the framework itself to introspect and document all functions, creating a complete reference wiki from code comments.
Quick start
Generate or update the wiki documentation:
./gen_wiki.sh ./compiled.sh ./wiki
Output:
bash-framehead wiki generator
Source: ./compiled.sh
Output: ./wiki
Found 785 functions across 16 modules
123 created, 662 skipped / 785 total
Generating module indexes...
string.md
array.md
math.md
...
Generating root index...
Done — 123 created, 662 skipped, ./wiki/README.md updated
How it works
The wiki generator dogfoods the framework to document itself:
From wiki-gen.sh:1-22:
#!/usr/bin/env bash
# gen_wiki.sh — generate wiki docs from bash-framehead compiled output
# Dogfoods the framework for string manipulation, fs operations, and path handling
#
# Usage: ./gen_wiki.sh <framework_file> [wiki_dir]
# Example: ./gen_wiki.sh ./bash-framehead.sh ./wiki
#
# Behaviour:
# - Function pages: skipped if already exist (preserves manual edits)
# - Module indexes: new entries appended, existing entries preserved
# - Root index: new module rows appended, existing rows preserved
SRC="${1:?Usage: gen_wiki.sh <framework_file> [wiki_dir]}"
WIKI="${2:-./wiki}"
[[ -f "$SRC" ]] || { echo "Error: source file not found: $SRC" >&2; exit 1; }
# Load the framework — this is the whole point
source "$SRC"
The script:
- Sources the compiled framework
- Uses
declare -F to introspect all functions
- Extracts comments using grep and string manipulation
- Generates markdown files using
fs::write
Introspection helpers
The generator uses the framework’s own string functions:
From wiki-gen.sh:27-41:
_wiki::all_functions() {
declare -F | awk '{print $3}' | grep -v '^_' | grep '::' | sort
}
_wiki::module_of() {
string::before "$1" "::"
}
_wiki::leaf_of() {
string::after "$1" "::"
}
_wiki::to_path() {
string::replace_all "$1" "::" "/"
}
These helpers:
- List all public functions (excluding
_internal ones)
- Extract module namespace from
module::function
- Convert
:: to / for file paths
The generator extracts documentation comments above function definitions:
From wiki-gen.sh:43-81:
# Extract comment block above a function definition
# Stops at blank lines, non-comment lines, and divider lines (# ===...)
_wiki::extract_comments() {
local funcname="$1"
local lineno
lineno=$(grep -n "^${funcname}()" "$SRC" | head -1 | cut -d: -f1)
[[ -z "$lineno" ]] && return
local i=$(( lineno - 1 ))
local -a lines=()
while (( i >= 1 )); do
local line
line=$(sed -n "${i}p" "$SRC")
# Stop at divider lines: # ===... or # ---...
if [[ "$line" =~ ^#[[:space:]]*[=\-]{4,}[[:space:]]*$ ]]; then
break
fi
if [[ "$line" =~ ^#[[:space:]]?(.*) ]]; then
local content="${BASH_REMATCH[1]}"
lines=("$content" "${lines[@]}")
(( i-- ))
elif [[ -z "$line" ]]; then
# Allow blank lines if surrounded by comments
local prev
prev=$(sed -n "$(( i - 1 ))p" "$SRC")
[[ "$prev" =~ ^# ]] && { (( i-- )); continue; }
break
else
break
fi
done
printf '%s\n' "${lines[@]}"
}
The comment extractor stops at divider lines (# === or # ---) to avoid including unrelated comments from module headers.
Function page generation
Each function gets its own markdown page:
From wiki-gen.sh:110-160:
_wiki::write_function_page() {
local funcname="$1"
local module leaf leaf_path out_file out_dir
module=$(_wiki::module_of "$funcname")
leaf=$(_wiki::leaf_of "$funcname")
leaf_path=$(_wiki::to_path "$leaf")
out_file="${WIKI}/${module}/${leaf_path}.md"
out_dir=$(fs::path::dirname "$out_file")
fs::mkdir "$out_dir"
# Skip if already exists — preserve manual edits
if [[ -f "$out_file" ]]; then
return
fi
local comments usage description example
comments=$(_wiki::extract_comments "$funcname")
usage=$(grep -i '^Usage:' <<< "$comments" | string::after_last "Usage: " || true)
example=$(grep -i '^Example:' <<< "$comments" | string::after_last "Example: " || true)
description=$(grep -iv '^Usage:\|^Example:' <<< "$comments" | grep -v '^$' | head -1 || true)
local body
body=$(_wiki::extract_body "$funcname")
fs::write "$out_file" "# \`${funcname}\`
${description:-_No description available._}
## Usage
\`\`\`bash
${usage:-${funcname} ...}
\`\`\`
## Source
\`\`\`bash
${body}
\`\`\`
## Module
[\`${module}\`](../${module}.md)
"
}
Preserves manual edits: Function pages are only created if they don’t exist. Existing files are never overwritten, allowing you to manually enhance documentation.
Module index generation
Module index pages list all functions in a module:
From wiki-gen.sh:162-192:
_wiki::write_module_index() {
local module="$1"
shift
local -a funcs=("$@")
local index_file="${WIKI}/${module}.md"
# Create with header if it doesn't exist yet
if [[ ! -f "$index_file" ]]; then
{
echo "# \`${module}\`"
echo ""
echo "| Function | Description |"
echo "|----------|-------------|"
} > "$index_file"
fi
local existing
existing=$(cat "$index_file")
local fn comments desc leaf leaf_path
for fn in "${funcs[@]}"; do
# Skip if this function already has an entry
string::contains "$existing" "\`${fn}\`" && continue
leaf=$(_wiki::leaf_of "$fn")
leaf_path=$(_wiki::to_path "$leaf")
comments=$(_wiki::extract_comments "$fn")
desc=$(grep -iv '^Usage:\|^Example:' <<< "$comments" | grep -v '^$' | head -1 || true)
echo "| [\`${fn}\`](./${module}/${leaf_path}.md) | ${desc:-—} |" >> "$index_file"
done
}
Index pages are append-only — new functions are added without modifying existing entries.
Root index generation
The root README.md provides an overview of all modules:
From wiki-gen.sh:194-221:
_wiki::write_root_index() {
local -a modules=("$@")
local index_file="${WIKI}/README.md"
# Create with header if it doesn't exist yet
if [[ ! -f "$index_file" ]]; then
{
echo "# bash-framehead"
echo ""
echo "## Modules"
echo ""
echo "| Module | Functions |"
echo "|--------|-----------|"
} > "$index_file"
fi
local existing
existing=$(cat "$index_file")
local m count
for m in $(printf '%s\n' "${modules[@]}" | sort -u); do
# Skip if module already has an entry
string::contains "$existing" "\`${m}\`" && continue
count=$(grep -c '^| `' "${WIKI}/${m}.md" 2>/dev/null || echo 0)
echo "| [\`${m}\`](./${m}.md) | ${count} |" >> "$index_file"
done
}
For optimal documentation generation, format comments like this:
# Brief one-line description of what the function does
# Usage: module::function arg1 arg2 [optional]
# Example: module::function hello world
module::function() {
# implementation
}
The generator extracts:
- Description: First non-Usage/Example comment line
- Usage: Text after
Usage: prefix
- Example: Text after
Example: prefix
First line: description
# Convert a string to uppercase
Used in module index tables and function page headers.Usage line
# Usage: string::upper str
Shows function signature with parameter names.Example line (optional)
# Example: string::upper "hello world" # HELLO WORLD
Demonstrates practical usage.Additional context (optional)
# This function uses pure Bash parameter expansion.
# Requires Bash 4.0+ for ${var^^} syntax.
Any extra comments become part of the description.
Generated wiki structure
The generator creates this structure:
wiki/
├── README.md # Root index with module overview
├── string.md # String module index
├── string/
│ ├── upper.md # string::upper function page
│ ├── lower.md # string::lower function page
│ ├── trim.md # string::trim function page
│ └── split.md # string::split function page
├── array.md # Array module index
├── array/
│ ├── length.md
│ ├── first.md
│ └── last.md
└── math.md # Math module index
└── math/
├── abs.md
└── factorial.md
Incremental updates
The generator is designed for incremental updates:
- Function pages: Created once, never overwritten
- Module indexes: New entries appended
- Root index: New modules appended
This means you can:
Run the generator repeatedly without losing manual edits
Add examples and detailed explanations to function pages
Regenerate after adding new functions
Customize module index introductions
Workflow:
# Initial generation
./gen_wiki.sh ./compiled.sh ./wiki
# → Creates all pages
# Manually enhance string::upper.md with detailed examples
vim wiki/string/upper.md
# Add new functions to the framework
vim src/string.sh
./main.sh compile
# Regenerate wiki
./gen_wiki.sh ./compiled.sh ./wiki
# → Adds new function pages, preserves your edits to existing ones
Body extraction
The generator also extracts complete function bodies for reference:
From wiki-gen.sh:84-104:
# Extract function body from source
_wiki::extract_body() {
local funcname="$1"
local lineno
lineno=$(grep -n "^${funcname}()" "$SRC" | head -1 | cut -d: -f1)
[[ -z "$lineno" ]] && return
local depth=0 started=false
local -a body=()
while IFS= read -r line; do
started=true
body+=("$line")
local opens closes
opens=$(grep -o '{' <<< "$line" | wc -l)
closes=$(grep -o '}' <<< "$line" | wc -l)
depth=$(( depth + opens - closes ))
$started && (( depth == 0 )) && break
done < <(tail -n "+${lineno}" "$SRC")
printf '%s\n' "${body[@]}"
}
This provides:
- Complete function implementation
- Syntax highlighting in markdown
- Reference for understanding internals
Main execution flow
From wiki-gen.sh:227-288:
fs::mkdir "$WIKI"
echo "bash-framehead wiki generator"
echo "Source: $SRC"
echo "Output: $WIKI"
echo ""
# Collect all functions grouped by module
declare -A module_funcs=()
declare -a all_funcs=()
while IFS= read -r fn; do
module=$(_wiki::module_of "$fn")
if [[ -z "${module_funcs[$module]+x}" ]]; then
module_funcs["$module"]="$fn"
else
module_funcs["$module"]+" $fn"
fi
all_funcs+=("$fn")
done < <(_wiki::all_functions)
total=${#all_funcs[@]}
module_count=${#module_funcs[@]}
echo "Found $total functions across $module_count modules"
echo ""
# Generate function pages
created=0
skipped=0
for fn in "${all_funcs[@]}"; do
# ... page generation ...
printf '\r %d created, %d skipped / %d total' "$created" "$skipped" "$total"
done
# Generate module indexes
for module in "${!module_funcs[@]}"; do
read -ra funcs <<< "${module_funcs[$module]}"
_wiki::write_module_index "$module" "${funcs[@]}"
done
# Generate root index
_wiki::write_root_index "${!module_funcs[@]}"
Best practices
Always compile first
Ensure the framework is compiled before generating docs:./main.sh compile
./gen_wiki.sh ./compiled.sh ./wiki
Use consistent comment format
Follow the Description/Usage/Example pattern:# What the function does
# Usage: module::function args
# Example: module::function value
Separate function sections with dividers
Use dividers to prevent comment bleed:# ==============================
# String validation functions
# ==============================
# Check if string is numeric
string::is_numeric() { ... }
Enhance generated pages manually
After initial generation, add:
- Detailed examples
- Performance notes
- Related functions
- Edge case documentation
Example function documentation
Well-documented function in source:
# Convert string to uppercase using pure Bash
# Usage: string::upper str
# Example: string::upper "hello" # Returns: HELLO
string::upper() {
echo "${1^^}"
}
Generated wiki page:
# `string::upper`
Convert string to uppercase using pure Bash
## Usage
```bash
string::upper str
Example
string::upper "hello" # Returns: HELLO
Source
string::upper() {
echo "${1^^}"
}
Module
string
## Next steps
<CardGroup cols={2}>
<Card title="Adding modules" icon="puzzle-piece" href="/development/adding-modules">
Create modules with proper documentation comments
</Card>
<Card title="Contributing" icon="code-pull-request" href="/development/contributing">
Submit your documented modules to the project
</Card>
</CardGroup>