aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-08 17:31:49 -0400
committerhistoria <historiavg@proton.me>2026-09-08 17:31:49 -0400
commitc63794d3da3c599cf08854d30c0cf6f2b4ed1986 (patch)
tree8cb2780b75ec4880646eab07563b1ca0831254ea
parent317033703195fb1c5695e36e5899fe0080fc441e (diff)
downloadssg-blog-c63794d3da3c599cf08854d30c0cf6f2b4ed1986.tar.gz
feat: explicitly listed and orderable categories
-rw-r--r--README.md10
-rw-r--r--demo/example.webpbin15022 -> 0 bytes
-rwxr-xr-xssg-blog107
3 files changed, 87 insertions, 30 deletions
diff --git a/README.md b/README.md
index 03ab255..d5e4db5 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ chmod +x /usr/bin/ssg-blog
ssg-blog src dst 'https://my-website.net'
```
-ssg-blog checks for subdirectories in the src directory, each of which becomes a category heading (non-recursively). It will generate a sorted list of links to any .md file in those directory (including nested directories).
+Blog categories are hard-coded in the `CATEGORIES` variable at the top of the script. Each one becomes a category heading in the order listed. Blog posts should be in a directory with the same name.
```
ssg-blog/
@@ -60,14 +60,6 @@ Note a few things about the output:
2. Files named index.md will get have a link generated without <filename>.html at the end.
3. Files outside subdirectories (e.g. about.md) will be rendered but won't show up on the index.
-## Change how categories are sorted
-
-The category headings are sorted alphabetically. If you want to change this, you have to edit the `sort` on this line:
-
-```
-find "$src" -mindepth 1 -maxdepth 1 -type d ! -path "*/.*" | sort |
-```
-
# Differences from ssg
The title argument from ssg is unnecessary so I've removed it. `render_html_file` used it to modify the first `<h1>` tag and auto-populate the `<title>` if none is specified, which is functionality I've never needed. Instead I check the frontmatter of each blog post and update the `<title>` tag with the `title:` field.
diff --git a/demo/example.webp b/demo/example.webp
deleted file mode 100644
index d9153d9..0000000
--- a/demo/example.webp
+++ /dev/null
Binary files differ
diff --git a/ssg-blog b/ssg-blog
index 398aa77..44997e0 100755
--- a/ssg-blog
+++ b/ssg-blog
@@ -16,6 +16,13 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
+# Blog categories: top-level source directories that make up the blog,
+# in the order they appear on the index page. .html files inside these
+# directories get the site header and footer; .html files anywhere else
+# are copied verbatim (e.g. standalone pages like JS tools). If empty,
+# every top-level directory is a category, sorted alphabetically.
+CATEGORIES="games lists money technology tutorials"
+
main() {
test -n "$1" || usage
test -n "$2" || usage
@@ -73,9 +80,13 @@ main() {
fi
echo "$files" | grep '\.html$' |
+ wrap_html |
render_html_files "$src" "$dst"
- echo "$files" | grep -Ev '\.md$|\.html$' |
+ {
+ echo "$files" | grep '\.html$' | verbatim_html
+ echo "$files" | grep -Ev '\.md$|\.html$'
+ } |
(cd "$src" && cpio -pu "$dst")
fi
@@ -192,6 +203,45 @@ render_html_file() {
}
}
+in_category_dir() {
+ f="${1#./}"
+ dir="${f%%/*}"
+ if [ "$dir" = "$f" ]; then
+ return 1
+ fi
+ for category in $CATEGORIES; do
+ if [ "$dir" = "$category" ]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+wrap_html() {
+ if [ -z "$CATEGORIES" ]; then
+ cat
+ return 0
+ fi
+ while read -r f; do
+ if in_category_dir "$f"; then
+ echo "$f"
+ fi
+ done
+ return 0
+}
+
+verbatim_html() {
+ if [ -z "$CATEGORIES" ]; then
+ return 0
+ fi
+ while read -r f; do
+ if ! in_category_dir "$f"; then
+ echo "$f"
+ fi
+ done
+ return 0
+}
+
list_pages() {
e="\\( -name '*.html' -o -name '*.md' \\)"
cd "$1" && eval "find . -type f ! -path '*/.*' ! -path '*/_*' $IGNORE $e" |
@@ -206,25 +256,40 @@ list_pages() {
sed 's#^./##;s#.md$#.html#;s#/index.html$#/#'
}
+list_category_articles() {
+ # $1 = src, $2 = category, $3 = category order
+ find "$1/$2" -type f -name "*.md" ! -path "*/.*" |
+ while read -r article; do
+ test "$(get_frontmatter_field "$article" "publish")" != "false" || continue
+ date=$(get_frontmatter_field "$article" "date")
+ test -n "$date" || continue
+ title=$(get_frontmatter_field "$article" "title")
+ url="${article#"$1"}"
+ url="${url%.md}.html"
+ if [ "${url##*/}" = "index.html" ]; then
+ url="${url%/index.html}/"
+ fi
+ url="${url#/}"
+ echo "$3|$2|$date|$title|$url"
+ done
+}
+
list_articles() {
- find "$1" -mindepth 1 -maxdepth 1 -type d ! -path "*/.*" | sort |
- while read -r category_dir; do
- category=$(basename "$category_dir")
- find "$category_dir" -type f \( -name "*.md" -o -name "index.md" \) ! -path "*/.*" |
- while read -r article; do
- test "$(get_frontmatter_field "$article" "publish")" != "false" || continue
- date=$(get_frontmatter_field "$article" "date")
- test -n "$date" || continue
- title=$(get_frontmatter_field "$article" "title")
- url="${article#"$1"}"
- url="${url%.md}.html"
- if [ "${url##*/}" = "index.html" ]; then
- url="${url%/index.html}/"
+ if [ -n "$CATEGORIES" ]; then
+ n=0
+ for category in $CATEGORIES; do
+ if [ -d "$1/$category" ]; then
+ n=$((n+1))
+ list_category_articles "$1" "$category" "$n"
fi
- url="${url#/}"
- echo "$category|$date|$title|$url"
done
- done
+ else
+ find "$1" -mindepth 1 -maxdepth 1 -type d ! -path "*/.*" | sort |
+ while read -r category_dir; do
+ n=$((n+1))
+ list_category_articles "$1" "$(basename "$category_dir")" "$n"
+ done
+ fi
}
render_index() {
@@ -232,8 +297,8 @@ render_index() {
echo "$HEADER" > "$output"
last_category=""
- list_articles "$src" | sort -t'|' -k1,1 -k2,2r |
- while IFS='|' read -r category date title url; do
+ list_articles "$src" | sort -t'|' -k1,1n -k3,3r |
+ while IFS='|' read -r order category date title url; do
if [ "$category" != "$last_category" ]; then
display_category=$(echo "$category" | sed 's/_/ /g' | awk '{for(i=1;i<=NF;i++){s=$i;$i=toupper(substr(s,1,1)) substr(s,2)}; print}')
echo "<h1>$display_category</h1>" >> "$output"
@@ -258,7 +323,7 @@ rfc822_date() {
}
render_rss() {
- list_articles "$src" | sort -t'|' -k2,2r |
+ list_articles "$src" | sort -t'|' -k3,3r |
{
echo '<?xml version="1.0" encoding="UTF-8"?>'
echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">'
@@ -268,7 +333,7 @@ render_rss() {
echo "<description>$base_url</description>"
echo "<atom:link href=\"$base_url/rss.xml\" rel=\"self\" type=\"application/rss+xml\"/>"
- while IFS='|' read -r category date title url; do
+ while IFS='|' read -r order category date title url; do
escaped_title=$(echo "$title" | xml_escape)
escaped_url=$(echo "$url" | xml_escape)
pub_date=$(rfc822_date "$date")