Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 930e1ac4dc | |||
| cd9550ee8a | |||
| 8b760b2897 | |||
| e3bf598dd9 | |||
| a086565ede | |||
| 79bc467bf6 | |||
| 8a5fa202de |
@@ -37,6 +37,7 @@ jobs:
|
||||
|
||||
- name: Deploy website
|
||||
run: |
|
||||
rsync -az --delete \
|
||||
rsync -rlz --delete --omit-dir-times \
|
||||
--exclude '/logs/' \
|
||||
-e "ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no" \
|
||||
out/ "${{ secrets.REMOTE_HOST }}:/var/www/kewt.krzak.org/"
|
||||
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Arch Linux environment
|
||||
- name: Ensure publish tools are available
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y pacman-package-manager curl jq || true
|
||||
sudo apt-get install -y openssh-client git
|
||||
|
||||
- name: Prepare AUR files
|
||||
run: |
|
||||
@@ -27,12 +27,23 @@ jobs:
|
||||
cp packaging/AUR/PKGBUILD.git aur-work/PKGBUILD
|
||||
cp packaging/AUR/.SRCINFO.git aur-work/.SRCINFO
|
||||
|
||||
- name: Setup SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
printf '%s\n' "${{ secrets.AUR_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Publish to AUR
|
||||
uses: KSXGitHub/github-actions-deploy-aur@v3.0.1
|
||||
with:
|
||||
pkgname: kewt-git
|
||||
pkgbuild: ./aur-work/PKGBUILD
|
||||
commit_username: ${{ github.actor }}
|
||||
commit_email: ${{ github.actor }}@users.noreply.github.com
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: "Update kewt-git to ${{ github.sha }}"
|
||||
run: |
|
||||
rm -rf aur-repo
|
||||
git clone "ssh://aur@aur.archlinux.org/kewt-git.git" aur-repo
|
||||
cp aur-work/PKGBUILD aur-repo/PKGBUILD
|
||||
cp aur-work/.SRCINFO aur-repo/.SRCINFO
|
||||
|
||||
cd aur-repo
|
||||
git config user.name "${{ github.actor }}"
|
||||
git config user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m "Update kewt-git to ${GITHUB_SHA}" || exit 0
|
||||
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes" git push origin HEAD:master
|
||||
|
||||
@@ -20,51 +20,80 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: "1.21"
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
files: |-
|
||||
kewt
|
||||
api_key: '${{secrets.GITEA_TOKEN}}'
|
||||
packaging/bash/kewt.bash
|
||||
api_key: "${{secrets.GITEA_TOKEN}}"
|
||||
|
||||
- name: Push to GitHub Release
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
|
||||
# Fetch release body from Gitea
|
||||
RELEASE_BODY=$(curl -sL \
|
||||
"https://git.krzak.org/api/v1/repos/N0VA/kewt/releases/tags/${TAG}" \
|
||||
| jq -r '.body // ""')
|
||||
|
||||
# Build JSON payload
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg tag "$TAG" \
|
||||
--arg name "Release $TAG" \
|
||||
--arg body "$RELEASE_BODY" \
|
||||
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')
|
||||
|
||||
# Create the release on GitHub
|
||||
curl -sL -X POST \
|
||||
-H "Authorization: token ${{ secrets.GH_RELEASE_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/n0va-bot/kewt/releases" \
|
||||
-d "$PAYLOAD" || true
|
||||
|
||||
# Get the release ID
|
||||
RELEASE_ID=$(curl -sL \
|
||||
-H "Authorization: token ${{ secrets.GH_RELEASE_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/n0va-bot/kewt/releases/tags/${TAG}" | jq -r '.id')
|
||||
|
||||
# Upload the asset
|
||||
curl -sL -X POST \
|
||||
-H "Authorization: token ${{ secrets.GH_RELEASE_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"https://uploads.github.com/repos/n0va-bot/kewt/releases/${RELEASE_ID}/assets?name=kewt" \
|
||||
--data-binary @kewt
|
||||
|
||||
publish-fedora:
|
||||
runs-on: local
|
||||
needs: build
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y rpm || true
|
||||
|
||||
- name: Build Fedora Assets
|
||||
run: |
|
||||
if command -v rpmbuild >/dev/null; then
|
||||
make srpm
|
||||
else
|
||||
echo "rpmbuild not found, generating spec file only"
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
VERSION="${TAG#v}"
|
||||
sed -e "s/VERSION_PLACEHOLDER/${VERSION}/g" packaging/fedora/kewt.spec.template > packaging/fedora/kewt.spec
|
||||
fi
|
||||
|
||||
- name: Upload Fedora Assets
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
files: |-
|
||||
kewt-*.src.rpm
|
||||
packaging/fedora/kewt.spec
|
||||
api_key: "${{secrets.GITEA_TOKEN}}"
|
||||
|
||||
publish-aur:
|
||||
runs-on: local
|
||||
needs: build
|
||||
@@ -75,7 +104,7 @@ jobs:
|
||||
- name: Setup Arch Linux environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y pacman-package-manager curl jq || true
|
||||
sudo apt-get install -y openssh-client curl jq
|
||||
|
||||
- name: Render PKGBUILD and SRCINFO
|
||||
run: |
|
||||
@@ -95,15 +124,26 @@ jobs:
|
||||
-e "s/SHA256SUM_PLACEHOLDER/${CHECKSUM}/g" \
|
||||
packaging/AUR/.SRCINFO.template > aur-work/.SRCINFO
|
||||
|
||||
- name: Setup AUR SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
printf '%s\n' "${{ secrets.AUR_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Publish to AUR
|
||||
uses: KSXGitHub/github-actions-deploy-aur@v3.0.1
|
||||
with:
|
||||
pkgname: kewt-bin
|
||||
pkgbuild: ./aur-work/PKGBUILD
|
||||
commit_username: ${{ github.actor }}
|
||||
commit_email: ${{ github.actor }}@users.noreply.github.com
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: "Update kewt-bin to ${{ github.ref_name }}"
|
||||
run: |
|
||||
rm -rf aur-repo
|
||||
git clone "ssh://aur@aur.archlinux.org/kewt-bin.git" aur-repo
|
||||
cp aur-work/PKGBUILD aur-repo/PKGBUILD
|
||||
cp aur-work/.SRCINFO aur-repo/.SRCINFO
|
||||
|
||||
cd aur-repo
|
||||
git config user.name "${{ github.actor }}"
|
||||
git config user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m "Update kewt-bin to ${GITHUB_REF_NAME}" || exit 0
|
||||
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes" git push origin HEAD:master
|
||||
|
||||
publish-homebrew:
|
||||
runs-on: local
|
||||
|
||||
1
.shellcheckrc
Normal file
1
.shellcheckrc
Normal file
@@ -0,0 +1 @@
|
||||
disable=SC2034,SC2154
|
||||
23
Makefile
23
Makefile
@@ -1,6 +1,7 @@
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
ZSHCOMPDIR ?= $(PREFIX)/share/zsh/site-functions
|
||||
BASHCOMPDIR ?= $(PREFIX)/share/bash-completion/completions
|
||||
|
||||
all: kewt
|
||||
|
||||
@@ -12,12 +13,30 @@ install: kewt
|
||||
install -m 755 kewt $(DESTDIR)$(BINDIR)/kewt
|
||||
install -d $(DESTDIR)$(ZSHCOMPDIR)
|
||||
install -m 644 packaging/zsh/_kewt $(DESTDIR)$(ZSHCOMPDIR)/_kewt
|
||||
install -d $(DESTDIR)$(BASHCOMPDIR)
|
||||
install -m 644 packaging/bash/kewt.bash $(DESTDIR)$(BASHCOMPDIR)/kewt
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(BINDIR)/kewt
|
||||
rm -f $(DESTDIR)$(ZSHCOMPDIR)/_kewt
|
||||
rm -f $(DESTDIR)$(BASHCOMPDIR)/kewt
|
||||
|
||||
clean:
|
||||
rm -f kewt
|
||||
rm -f kewt kewt-*.tar.gz
|
||||
|
||||
.PHONY: all install uninstall clean
|
||||
dist:
|
||||
$(eval VERSION := $(shell git describe --tags --always | sed 's/^v//;s/-/./g'))
|
||||
tar -czf kewt-$(VERSION).tar.gz --exclude-vcs --exclude=kewt --exclude=kewt-$(VERSION).tar.gz --transform "s|^|kewt-$(VERSION)/|" *
|
||||
|
||||
srpm: dist
|
||||
$(eval VERSION := $(shell git describe --tags --always | sed 's/^v//;s/-/./g'))
|
||||
sed -e "s/VERSION_PLACEHOLDER/$(VERSION)/g" packaging/fedora/kewt.spec.template > packaging/fedora/kewt.spec
|
||||
rpmbuild -bs --define "_sourcedir $(PWD)" --define "_srcrpmdir $(PWD)" packaging/fedora/kewt.spec
|
||||
|
||||
test:
|
||||
sh tests/test_runner.sh
|
||||
|
||||
shellcheck:
|
||||
shellcheck kewt.sh markdown.sh lib/*.sh
|
||||
|
||||
.PHONY: all install uninstall clean test shellcheck
|
||||
|
||||
@@ -23,10 +23,17 @@ function unique_id(raw_id, candidate) {
|
||||
seen_ids[candidate]++
|
||||
return candidate "-" seen_ids[candidate]
|
||||
}
|
||||
function has_inline_link(line) {
|
||||
return (index(line, "<a ") > 0 || index(line, "<a\t") > 0)
|
||||
}
|
||||
function print_heading(tag, line, id) {
|
||||
id = unique_id(strip_markdown(line))
|
||||
if (enable_header_links == "true") {
|
||||
print "<" tag " id=\"" id "\">" line " <a href=\"#" id "\" class=\"header-anchor\" aria-label=\"Link to this section\">#</a></" tag ">"
|
||||
if (has_inline_link(line)) {
|
||||
print "<" tag " id=\"" id "\">" line " <a href=\"#" id "\" class=\"header-anchor\" aria-label=\"Link to this section\">#</a></" tag ">"
|
||||
} else {
|
||||
print "<" tag " id=\"" id "\"><a href=\"#" id "\" class=\"header-link\">" line "<span class=\"header-anchor\" aria-hidden=\"true\">#</span></a></" tag ">"
|
||||
}
|
||||
} else {
|
||||
print "<" tag " id=\"" id "\">" line "</" tag ">"
|
||||
}
|
||||
|
||||
119
awk/reference_links.awk
Normal file
119
awk/reference_links.awk
Normal file
@@ -0,0 +1,119 @@
|
||||
{
|
||||
lines[NR] = $0
|
||||
total = NR
|
||||
|
||||
if (/^\[[^\]]+\]: */) {
|
||||
line = $0
|
||||
sub(/^\[/, "", line)
|
||||
ref_id = line
|
||||
sub(/\].*/, "", ref_id)
|
||||
|
||||
line = $0
|
||||
sub(/^\[[^\]]+\]: */, "", line)
|
||||
ref_url = line
|
||||
sub(/[ \t].*/, "", ref_url)
|
||||
|
||||
ref_title = $0
|
||||
sub(/^\[[^\]]+\]: *[^\t ]*[ \t]*/, "", ref_title)
|
||||
sub(/^"/, "", ref_title)
|
||||
sub(/"$/, "", ref_title)
|
||||
gsub(/\|/, "!", ref_title)
|
||||
|
||||
refs[ref_id] = ref_url
|
||||
if (ref_title != "") titles[ref_id] = ref_title
|
||||
is_ref[NR] = 1
|
||||
}
|
||||
}
|
||||
|
||||
function resolve_image_ref(alt, id, url, title) {
|
||||
url = refs[id]
|
||||
title = (id in titles) ? titles[id] : ""
|
||||
if (url == "") return "![" alt "][" id "]"
|
||||
return "<img src=\"" url "\" title=\"" title "\" alt=\"" alt "\" />"
|
||||
}
|
||||
|
||||
function resolve_link_ref(text, id, url, title) {
|
||||
url = refs[id]
|
||||
title = (id in titles) ? titles[id] : ""
|
||||
if (url == "") return "[" text "][" id "]"
|
||||
return "<a href=\"" url "\" title=\"" title "\">" text "</a>"
|
||||
}
|
||||
|
||||
function process_refs(line, result, i, len, ch, j, k, depth, bracket_content, ref_id) {
|
||||
result = ""
|
||||
len = length(line)
|
||||
i = 1
|
||||
|
||||
while (i <= len) {
|
||||
ch = substr(line, i, 1)
|
||||
|
||||
if (ch == "!" && i < len && substr(line, i + 1, 1) == "[") {
|
||||
bracket_content = ""
|
||||
j = i + 2
|
||||
while (j <= len && substr(line, j, 1) != "]") {
|
||||
bracket_content = bracket_content substr(line, j, 1)
|
||||
j++
|
||||
}
|
||||
if (j <= len && j < len && substr(line, j + 1, 1) == "[") {
|
||||
k = j + 2
|
||||
ref_id = ""
|
||||
while (k <= len && substr(line, k, 1) != "]") {
|
||||
ref_id = ref_id substr(line, k, 1)
|
||||
k++
|
||||
}
|
||||
if (k <= len) {
|
||||
if (ref_id == "") ref_id = bracket_content
|
||||
if (ref_id in refs) {
|
||||
result = result resolve_image_ref(bracket_content, ref_id)
|
||||
i = k + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
result = result substr(line, i, 1)
|
||||
i++
|
||||
} else if (ch == "[") {
|
||||
bracket_content = ""
|
||||
j = i + 1
|
||||
depth = 1
|
||||
while (j <= len && depth > 0) {
|
||||
if (substr(line, j, 1) == "[") depth++
|
||||
if (substr(line, j, 1) == "]") {
|
||||
depth--
|
||||
if (depth == 0) break
|
||||
}
|
||||
if (depth > 0) bracket_content = bracket_content substr(line, j, 1)
|
||||
j++
|
||||
}
|
||||
if (j <= len && j < len && substr(line, j + 1, 1) == "[") {
|
||||
k = j + 2
|
||||
ref_id = ""
|
||||
while (k <= len && substr(line, k, 1) != "]") {
|
||||
ref_id = ref_id substr(line, k, 1)
|
||||
k++
|
||||
}
|
||||
if (k <= len) {
|
||||
if (ref_id == "") ref_id = bracket_content
|
||||
if (ref_id in refs) {
|
||||
result = result resolve_link_ref(bracket_content, ref_id)
|
||||
i = k + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
result = result substr(line, i, 1)
|
||||
i++
|
||||
} else {
|
||||
result = result ch
|
||||
i++
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
END {
|
||||
for (n = 1; n <= total; n++) {
|
||||
if (is_ref[n]) continue
|
||||
print process_refs(lines[n])
|
||||
}
|
||||
}
|
||||
24
kewt.sh
24
kewt.sh
@@ -45,6 +45,8 @@ positional_count=0
|
||||
watch_mode="false"
|
||||
serve_mode="false"
|
||||
serve_port=""
|
||||
draft_mode="false"
|
||||
dry_run_mode="false"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
@@ -89,6 +91,8 @@ _kewt() {
|
||||
'--to[Output directory]:directory:_directories'
|
||||
'(-w --watch)'{-w,--watch}'[Watch for file changes and rebuild automatically]'
|
||||
'(-s --serve)'{-s,--serve}'[Start a local HTTP server after building]::port:'
|
||||
'(-d --draft)'{-d,--draft}'[Include draft pages in the build]'
|
||||
'(-)--dry-run[Show what would be built without writing any files]'
|
||||
)
|
||||
|
||||
_arguments -S -C $args '*: :_directories'
|
||||
@@ -141,6 +145,12 @@ EOFCOMPS
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--draft|-d)
|
||||
draft_mode="true"
|
||||
;;
|
||||
--dry-run)
|
||||
dry_run_mode="true"
|
||||
;;
|
||||
--*)
|
||||
die "Unknown option: $1"
|
||||
;;
|
||||
@@ -201,10 +211,24 @@ refresh_build_context
|
||||
if [ "$clean_mode" = "true" ]; then
|
||||
[ -d "$out" ] && rm -rf "$out"
|
||||
fi
|
||||
|
||||
if [ "$dry_run_mode" = "true" ]; then
|
||||
dry_run_out="$KEWT_TMPDIR/dry_run_out"
|
||||
mkdir -p "$dry_run_out"
|
||||
out="$dry_run_out"
|
||||
fi
|
||||
|
||||
mkdir -p "$out"
|
||||
|
||||
build_site
|
||||
|
||||
if [ "$dry_run_mode" = "true" ]; then
|
||||
echo ""
|
||||
echo "Dry run complete. Files that would be generated:"
|
||||
find "$dry_run_out" -type f | sed "s|^$dry_run_out/||" | sort
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$serve_mode" = "true" ]; then
|
||||
port="${serve_port:-8000}"
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
|
||||
762
lib/builder.sh
762
lib/builder.sh
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2129
|
||||
|
||||
needs_rebuild() {
|
||||
src_file="$1"
|
||||
@@ -29,65 +30,106 @@ write_content_warning_outputs() {
|
||||
generate_content_warning_page "$fm_title" "$fm_content_warning" "$_content_rel_url" "$_target_url" "$_landing_out_file" "false"
|
||||
}
|
||||
|
||||
build_site() {
|
||||
echo "Building site from '$src' to '$out'..."
|
||||
build_dir_entries_list() {
|
||||
_bde_dir="$1"
|
||||
_bde_rel_dir="$2"
|
||||
_bde_entries_file="$3"
|
||||
|
||||
build_markdown_manifest
|
||||
build_full_nav
|
||||
find "$_bde_dir" ! -name "$(basename "$_bde_dir")" -prune ! -name ".*" -print | while read -r entry; do
|
||||
name="${entry##*/}"
|
||||
case "$name" in
|
||||
template.html|site.conf|style.css|styles.root.css|index.md) continue ;;
|
||||
esac
|
||||
if [ -d "$entry" ]; then
|
||||
entry_rel_dir="${entry#"$src"/}"
|
||||
manifest_dir_hidden_by_draft_index "$entry_rel_dir" && continue
|
||||
dir_url="$(encode_url_path "$name")/index.html"
|
||||
echo "${name}|- [${name}/](${dir_url})" >> "$_bde_entries_file"
|
||||
elif [ "${entry%.md}" != "$entry" ]; then
|
||||
entry_rel_path="${entry#"$src"/}"
|
||||
load_manifest_entry "$entry_rel_path" || continue
|
||||
if [ "${draft_mode:-false}" != "true" ]; then
|
||||
[ "$manifest_draft" = "true" ] && continue
|
||||
fi
|
||||
label="${name%.md}"
|
||||
|
||||
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do
|
||||
rel_dir="${dir#"$src"}"
|
||||
rel_dir="${rel_dir#/}"
|
||||
[ -z "$rel_dir" ] && rel_dir="."
|
||||
out_dir="$out/$rel_dir"
|
||||
mkdir -p "$out_dir"
|
||||
post_h="$manifest_title"
|
||||
|
||||
if [ -f "$dir/styles.css" ]; then
|
||||
if needs_rebuild "$dir/styles.css" "$out_dir/styles.css"; then
|
||||
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
|
||||
is_post_entry="false"
|
||||
if is_posts_directory_rel "$_bde_rel_dir"; then
|
||||
is_post_entry="true"
|
||||
fi
|
||||
|
||||
if [ -n "$post_h" ]; then
|
||||
if [ "$is_post_entry" = "true" ]; then
|
||||
if [ -n "$manifest_post_time" ]; then
|
||||
label="$post_h - $manifest_post_date $manifest_post_time"
|
||||
else
|
||||
label="$post_h - $manifest_post_date"
|
||||
fi
|
||||
else
|
||||
label="$post_h"
|
||||
fi
|
||||
elif [ "$is_post_entry" = "true" ]; then
|
||||
if [ -n "$manifest_post_time" ]; then
|
||||
label="$manifest_post_date $manifest_post_time"
|
||||
else
|
||||
label="$manifest_post_date"
|
||||
fi
|
||||
fi
|
||||
if [ "$is_post_entry" = "true" ]; then
|
||||
sort_key="${manifest_post_date} ${manifest_post_time}"
|
||||
else
|
||||
sort_key="$name"
|
||||
fi
|
||||
entry_url=$(encode_url_path "${name%.md}.html")
|
||||
echo "${sort_key}|- [$label](${entry_url})|$name|${entry_url}" >> "$_bde_entries_file"
|
||||
else
|
||||
asset_url=$(encode_url_path "$name")
|
||||
echo "${name}|- [$name]($asset_url)|$name|$asset_url" >> "$_bde_entries_file"
|
||||
fi
|
||||
elif [ -f "$dir/style.css" ]; then
|
||||
if needs_rebuild "$dir/style.css" "$out_dir/styles.css"; then
|
||||
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
[ "$dir_indexes" != "true" ] && continue
|
||||
build_dir_index() {
|
||||
_bdi_dir="$1"
|
||||
_bdi_rel_dir="$2"
|
||||
_bdi_out_dir="$3"
|
||||
|
||||
has_custom_index="false"
|
||||
has_list="false"
|
||||
if [ -f "$dir/index.md" ]; then
|
||||
if [ -f "$_bdi_dir/index.md" ]; then
|
||||
has_custom_index="true"
|
||||
if grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$dir/index.md" 2>/dev/null; then
|
||||
if grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$_bdi_dir/index.md" 2>/dev/null; then
|
||||
has_list="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$has_custom_index" = "false" ] || [ "$has_list" = "true" ]; then
|
||||
is_posts_dir="false"
|
||||
if is_posts_directory_rel "$rel_dir"; then
|
||||
if is_posts_directory_rel "$_bdi_rel_dir"; then
|
||||
is_posts_dir="true"
|
||||
fi
|
||||
if [ "$single_file_index" = "true" ] && [ "$is_posts_dir" = "false" ] && [ "$has_list" = "false" ]; then
|
||||
if load_manifest_dir_entry "$rel_dir" && [ "$dir_md_count" -eq 1 ]; then
|
||||
if load_manifest_dir_entry "$_bdi_rel_dir" && [ "$dir_md_count" -eq 1 ]; then
|
||||
md_file="$src/$dir_first_md"
|
||||
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||
target_url=$(directory_index_url "$rel_dir")
|
||||
if needs_rebuild "$md_file" "$out_dir/index.html"; then
|
||||
is_home="false"; [ "$_bdi_dir" = "$src" ] && is_home="true"
|
||||
target_url=$(directory_index_url "$_bdi_rel_dir")
|
||||
if needs_rebuild "$md_file" "$_bdi_out_dir/index.html"; then
|
||||
parse_frontmatter "$md_file"
|
||||
if [ -n "$fm_content_warning" ]; then
|
||||
content_out_file="$out_dir/content.html"
|
||||
if [ "$rel_dir" = "." ]; then
|
||||
content_out_file="$_bdi_out_dir/content.html"
|
||||
if [ "$_bdi_rel_dir" = "." ]; then
|
||||
content_rel_url="/content.html"
|
||||
else
|
||||
content_rel_url="/$(encode_url_path "$rel_dir")/content.html"
|
||||
content_rel_url="/$(encode_url_path "$_bdi_rel_dir")/content.html"
|
||||
fi
|
||||
write_content_warning_outputs "$md_file" "$content_out_file" "$content_rel_url" "$target_url" "$out_dir/index.html" "$is_home"
|
||||
write_content_warning_outputs "$md_file" "$content_out_file" "$content_rel_url" "$target_url" "$_bdi_out_dir/index.html" "$is_home"
|
||||
else
|
||||
render_markdown "$md_file" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
render_markdown "$md_file" "$is_home" "$target_url" > "$_bdi_out_dir/index.html"
|
||||
fi
|
||||
fi
|
||||
continue
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -96,74 +138,21 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
: > "$temp_list"
|
||||
|
||||
if [ "$has_custom_index" = "false" ]; then
|
||||
display_dir="${rel_dir#.}"
|
||||
display_dir="${_bdi_rel_dir#.}"
|
||||
[ -z "$display_dir" ] && display_dir="/"
|
||||
echo "# Index of $display_dir" > "$temp_index"
|
||||
echo "" >> "$temp_index"
|
||||
fi
|
||||
|
||||
|
||||
sort_args=""
|
||||
# If this is the posts dir reverse
|
||||
if is_posts_directory_rel "$rel_dir"; then
|
||||
if is_posts_directory_rel "$_bdi_rel_dir"; then
|
||||
sort_args="-r"
|
||||
fi
|
||||
|
||||
temp_entries="$KEWT_TMPDIR/entries_$$.txt"
|
||||
: > "$temp_entries"
|
||||
|
||||
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | while read -r entry; do
|
||||
name="${entry##*/}"
|
||||
case "$name" in
|
||||
template.html|site.conf|style.css|styles.root.css|index.md) continue ;;
|
||||
esac
|
||||
if [ -d "$entry" ]; then
|
||||
entry_rel_dir="${entry#"$src"/}"
|
||||
manifest_dir_hidden_by_draft_index "$entry_rel_dir" && continue
|
||||
dir_url="$(encode_url_path "$name")/index.html"
|
||||
echo "${name}|- [${name}/](${dir_url})" >> "$temp_entries"
|
||||
elif [ "${entry%.md}" != "$entry" ]; then
|
||||
entry_rel_path="${entry#"$src"/}"
|
||||
load_manifest_entry "$entry_rel_path" || continue
|
||||
label="${name%.md}"
|
||||
[ "$manifest_draft" = "true" ] && continue
|
||||
|
||||
post_h="$manifest_title"
|
||||
|
||||
is_post_entry="false"
|
||||
if is_posts_directory_rel "$rel_dir"; then
|
||||
is_post_entry="true"
|
||||
fi
|
||||
|
||||
if [ -n "$post_h" ]; then
|
||||
if [ "$is_post_entry" = "true" ]; then
|
||||
if [ -n "$manifest_post_time" ]; then
|
||||
label="$post_h - $manifest_post_date $manifest_post_time"
|
||||
else
|
||||
label="$post_h - $manifest_post_date"
|
||||
fi
|
||||
else
|
||||
label="$post_h"
|
||||
fi
|
||||
elif [ "$is_post_entry" = "true" ]; then
|
||||
if [ -n "$manifest_post_time" ]; then
|
||||
label="$manifest_post_date $manifest_post_time"
|
||||
else
|
||||
label="$manifest_post_date"
|
||||
fi
|
||||
fi
|
||||
if [ "$is_post_entry" = "true" ]; then
|
||||
sort_key="${manifest_post_date} ${manifest_post_time}"
|
||||
else
|
||||
sort_key="$name"
|
||||
fi
|
||||
entry_url=$(encode_url_path "${name%.md}.html")
|
||||
echo "${sort_key}|- [$label](${entry_url})|$name|${entry_url}" >> "$temp_entries"
|
||||
else
|
||||
asset_url=$(encode_url_path "$name")
|
||||
echo "${name}|- [$name]($asset_url)|$name|$asset_url" >> "$temp_entries"
|
||||
fi
|
||||
done
|
||||
build_dir_entries_list "$_bdi_dir" "$_bdi_rel_dir" "$temp_entries"
|
||||
|
||||
if [ "$is_posts_dir" = "true" ]; then
|
||||
LC_ALL=C sort $sort_args "$temp_entries" > "$KEWT_TMPDIR/sorted_entries_$$.txt"
|
||||
@@ -197,207 +186,244 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
fi
|
||||
rm -f "$temp_entries"
|
||||
|
||||
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||
target_url=$(directory_index_url "$rel_dir")
|
||||
is_home="false"; [ "$_bdi_dir" = "$src" ] && is_home="true"
|
||||
target_url=$(directory_index_url "$_bdi_rel_dir")
|
||||
|
||||
num_items=$(wc -l < "$temp_list")
|
||||
if [ "$is_posts_dir" = "true" ] && [ -n "$posts_per_page" ] && [ "$posts_per_page" -gt 0 ] && [ "$num_items" -gt "$posts_per_page" ]; then
|
||||
num_pages=$(( (num_items + posts_per_page - 1) / posts_per_page ))
|
||||
p=1
|
||||
while [ "$p" -le "$num_pages" ]; do
|
||||
chunk_list="$KEWT_TMPDIR/chunk.md"
|
||||
start_line=$(( (p - 1) * posts_per_page + 1 ))
|
||||
tail -n +$start_line "$temp_list" | head -n "$posts_per_page" > "$chunk_list"
|
||||
render_paginated_index "$_bdi_dir" "$_bdi_rel_dir" "$_bdi_out_dir" "$temp_index" "$temp_list" "$target_url" "$is_home" "$has_custom_index" "$is_posts_dir"
|
||||
rm -f "$temp_index" "$temp_list"
|
||||
fi
|
||||
}
|
||||
|
||||
base_url_dir="$(dirname "$target_url")"
|
||||
[ "$base_url_dir" = "/" ] && base_url_dir=""
|
||||
render_paginated_index() {
|
||||
_rpi_dir="$1"
|
||||
_rpi_rel_dir="$2"
|
||||
_rpi_out_dir="$3"
|
||||
_rpi_temp_index="$4"
|
||||
_rpi_temp_list="$5"
|
||||
_rpi_target_url="$6"
|
||||
_rpi_is_home="$7"
|
||||
_rpi_has_custom_index="$8"
|
||||
_rpi_is_posts_dir="$9"
|
||||
|
||||
nav_html="<div class=\"pagination\">"
|
||||
if [ "$p" -gt 1 ]; then
|
||||
if [ "$p" -eq 2 ]; then
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/index.html\" class=\"prev-page\">« Prev</a> "
|
||||
else
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/page/$((p-1))/index.html\" class=\"prev-page\">« Prev</a> "
|
||||
fi
|
||||
fi
|
||||
nav_html="$nav_html <span class=\"page-number\">Page $p of $num_pages</span> "
|
||||
if [ "$p" -lt "$num_pages" ]; then
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/page/$((p+1))/index.html\" class=\"next-page\">Next »</a> "
|
||||
fi
|
||||
nav_html="$nav_html</div>"
|
||||
num_items=$(wc -l < "$_rpi_temp_list")
|
||||
if [ "$_rpi_is_posts_dir" = "true" ] && [ -n "$posts_per_page" ] && [ "$posts_per_page" -gt 0 ] && [ "$num_items" -gt "$posts_per_page" ]; then
|
||||
num_pages=$(( (num_items + posts_per_page - 1) / posts_per_page ))
|
||||
p=1
|
||||
while [ "$p" -le "$num_pages" ]; do
|
||||
chunk_list="$KEWT_TMPDIR/chunk.md"
|
||||
start_line=$(( (p - 1) * posts_per_page + 1 ))
|
||||
tail -n +$start_line "$_rpi_temp_list" | head -n "$posts_per_page" > "$chunk_list"
|
||||
|
||||
echo "" >> "$chunk_list"
|
||||
echo "$nav_html" >> "$chunk_list"
|
||||
base_url_dir="$(dirname "$_rpi_target_url")"
|
||||
[ "$base_url_dir" = "/" ] && base_url_dir=""
|
||||
|
||||
temp_index_p="$KEWT_TMPDIR/index_p$p.md"
|
||||
if [ "$has_custom_index" = "false" ]; then
|
||||
display_dir="${rel_dir#.}"
|
||||
[ -z "$display_dir" ] && display_dir="/"
|
||||
echo "# Index of $display_dir" > "$temp_index_p"
|
||||
echo "" >> "$temp_index_p"
|
||||
nav_html="<div class=\"pagination\">"
|
||||
if [ "$p" -gt 1 ]; then
|
||||
if [ "$p" -eq 2 ]; then
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/index.html\" class=\"prev-page\">« Prev</a> "
|
||||
else
|
||||
: > "$temp_index_p"
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/page/$((p-1))/index.html\" class=\"prev-page\">« Prev</a> "
|
||||
fi
|
||||
fi
|
||||
nav_html="$nav_html <span class=\"page-number\">Page $p of $num_pages</span> "
|
||||
if [ "$p" -lt "$num_pages" ]; then
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/page/$((p+1))/index.html\" class=\"next-page\">Next »</a> "
|
||||
fi
|
||||
nav_html="$nav_html</div>"
|
||||
|
||||
if [ "$has_custom_index" = "true" ]; then
|
||||
awk '
|
||||
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
|
||||
while((getline line < "'"$chunk_list"'") > 0) print line
|
||||
close("'"$chunk_list"'")
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$dir/index.md" >> "$temp_index_p"
|
||||
else
|
||||
cat "$chunk_list" >> "$temp_index_p"
|
||||
fi
|
||||
echo "" >> "$chunk_list"
|
||||
echo "$nav_html" >> "$chunk_list"
|
||||
|
||||
if [ "$p" -eq 1 ]; then
|
||||
out_file="$out_dir/index.html"
|
||||
target_url_p="$target_url"
|
||||
else
|
||||
out_file="$out_dir/page/$p/index.html"
|
||||
target_url_p="$base_url_dir/page/$p/index.html"
|
||||
mkdir -p "$(dirname "$out_file")"
|
||||
fi
|
||||
temp_index_p="$KEWT_TMPDIR/index_p$p.md"
|
||||
if [ "$_rpi_has_custom_index" = "false" ]; then
|
||||
display_dir="${_rpi_rel_dir#.}"
|
||||
[ -z "$display_dir" ] && display_dir="/"
|
||||
echo "# Index of $display_dir" > "$temp_index_p"
|
||||
echo "" >> "$temp_index_p"
|
||||
else
|
||||
: > "$temp_index_p"
|
||||
fi
|
||||
|
||||
render_markdown "$temp_index_p" "$is_home" "$target_url_p" > "$out_file"
|
||||
rm -f "$temp_index_p" "$chunk_list"
|
||||
p=$((p + 1))
|
||||
done
|
||||
else
|
||||
if [ "$has_custom_index" = "true" ]; then
|
||||
if [ "$_rpi_has_custom_index" = "true" ]; then
|
||||
awk '
|
||||
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
|
||||
while((getline line < "'"$temp_list"'") > 0) print line
|
||||
close("'"$temp_list"'")
|
||||
while((getline line < "'"$chunk_list"'") > 0) print line
|
||||
close("'"$chunk_list"'")
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$dir/index.md" > "$temp_index"
|
||||
' "$_rpi_dir/index.md" >> "$temp_index_p"
|
||||
else
|
||||
cat "$temp_list" >> "$temp_index"
|
||||
cat "$chunk_list" >> "$temp_index_p"
|
||||
fi
|
||||
|
||||
do_rebuild="false"
|
||||
needs_rebuild "$dir" "$out_dir/index.html" && do_rebuild="true"
|
||||
[ "$has_custom_index" = "true" ] && needs_rebuild "$dir/index.md" "$out_dir/index.html" && do_rebuild="true"
|
||||
|
||||
if [ "$do_rebuild" = "false" ] && [ -f "$out_dir/index.html" ]; then
|
||||
for _child in "$dir"/*; do
|
||||
[ -e "$_child" ] || continue
|
||||
if [ "$_child" -nt "$out_dir/index.html" ]; then
|
||||
do_rebuild="true"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$do_rebuild" = "true" ]; then
|
||||
if [ "$has_custom_index" = "true" ]; then
|
||||
parse_frontmatter "$dir/index.md"
|
||||
else
|
||||
fm_content_warning=""
|
||||
fi
|
||||
|
||||
if [ -n "$fm_content_warning" ]; then
|
||||
content_out_file="$out_dir/content.html"
|
||||
if [ "$rel_dir" = "." ]; then
|
||||
content_rel_url="/content.html"
|
||||
else
|
||||
content_rel_url="/$(encode_url_path "$rel_dir")/content.html"
|
||||
fi
|
||||
write_content_warning_outputs "$temp_index" "$content_out_file" "$content_rel_url" "$target_url" "$out_dir/index.html" "$is_home"
|
||||
else
|
||||
render_markdown "$temp_index" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
rm -f "$temp_index" "$temp_list"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -f "$src/styles.css" ] && [ ! -f "$src/style.css" ]; then
|
||||
if [ -f "$src/styles.root.css" ]; then
|
||||
_base_css="$script_dir/styles/$style.css"
|
||||
[ ! -f "$_base_css" ] && _base_css="$script_dir/styles/kewt.css"
|
||||
if [ ! -f "$out/styles.css" ] || [ "$src/styles.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
|
||||
merge_root_style "$src/styles.root.css" "$_base_css" "$out/styles.css"
|
||||
fi
|
||||
elif [ -f "$script_dir/styles/$style.css" ]; then
|
||||
if needs_rebuild "$script_dir/styles/$style.css" "$out/styles.css"; then
|
||||
copy_style_with_resolved_vars "$script_dir/styles/$style.css" "$out/styles.css"
|
||||
fi
|
||||
elif [ -f "$script_dir/styles/$style.root.css" ]; then
|
||||
_base_css="$script_dir/styles/kewt.css"
|
||||
if [ ! -f "$out/styles.css" ] || [ "$script_dir/styles/$style.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
|
||||
merge_root_style "$script_dir/styles/$style.root.css" "$_base_css" "$out/styles.css"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
|
||||
rel_path="${file#"$src"}"
|
||||
rel_path="${rel_path#/}"
|
||||
dir_rel=$(dirname "$rel_path")
|
||||
out_dir="$out/$dir_rel"
|
||||
|
||||
case "${file##*/}" in
|
||||
template.html|site.conf|style.css|styles.css|styles.root.css) continue ;;
|
||||
esac
|
||||
|
||||
if [ "${file##*/}" = "index.md" ] && grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$file" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
|
||||
is_preserved=0
|
||||
if [ -n "$(eval "find \"$file\" \( $PRESERVE_ARGS \) -print")" ]; then
|
||||
is_preserved=1
|
||||
fi
|
||||
|
||||
is_posts_dir_2="false"
|
||||
if is_posts_directory_rel "$dir_rel"; then
|
||||
is_posts_dir_2="true"
|
||||
fi
|
||||
|
||||
if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ] && [ ! -f "$(dirname "$file")/index.md" ] && [ "$is_posts_dir_2" = "false" ]; then
|
||||
load_manifest_dir_entry "$dir_rel" && [ "$dir_md_count" -eq 1 ] && continue
|
||||
fi
|
||||
|
||||
if [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ]; then
|
||||
load_manifest_entry "$rel_path" || continue
|
||||
[ "$manifest_draft" = "true" ] && continue
|
||||
is_home="false"; [ "$file" = "$src/index.md" ] && is_home="true"
|
||||
out_file="$out/${rel_path%.md}.html"
|
||||
if needs_rebuild "$file" "$out_file"; then
|
||||
fm_title="$manifest_title"
|
||||
fm_content_warning="$manifest_content_warning"
|
||||
if [ -n "$manifest_content_warning" ]; then
|
||||
content_out_file="$out/${rel_path%.md}-content.html"
|
||||
content_rel_url="/$(encode_url_path "${rel_path%.md}")-content.html"
|
||||
orig_rel_url="$manifest_url"
|
||||
write_content_warning_outputs "$file" "$content_out_file" "$content_rel_url" "$orig_rel_url" "$out_file" "$is_home"
|
||||
if [ "$p" -eq 1 ]; then
|
||||
out_file="$_rpi_out_dir/index.html"
|
||||
target_url_p="$_rpi_target_url"
|
||||
else
|
||||
render_markdown "$file" "$is_home" > "$out_file"
|
||||
out_file="$_rpi_out_dir/page/$p/index.html"
|
||||
target_url_p="$base_url_dir/page/$p/index.html"
|
||||
mkdir -p "$(dirname "$out_file")"
|
||||
fi
|
||||
fi
|
||||
|
||||
render_markdown "$temp_index_p" "$_rpi_is_home" "$target_url_p" > "$out_file"
|
||||
rm -f "$temp_index_p" "$chunk_list"
|
||||
p=$((p + 1))
|
||||
done
|
||||
else
|
||||
if needs_rebuild "$file" "$out/$rel_path"; then
|
||||
cp "$file" "$out/$rel_path"
|
||||
if [ "$_rpi_has_custom_index" = "true" ]; then
|
||||
awk '
|
||||
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
|
||||
while((getline line < "'"$_rpi_temp_list"'") > 0) print line
|
||||
close("'"$_rpi_temp_list"'")
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$_rpi_dir/index.md" > "$_rpi_temp_index"
|
||||
else
|
||||
cat "$_rpi_temp_list" >> "$_rpi_temp_index"
|
||||
fi
|
||||
|
||||
do_rebuild="false"
|
||||
needs_rebuild "$_rpi_dir" "$_rpi_out_dir/index.html" && do_rebuild="true"
|
||||
[ "$_rpi_has_custom_index" = "true" ] && needs_rebuild "$_rpi_dir/index.md" "$_rpi_out_dir/index.html" && do_rebuild="true"
|
||||
|
||||
if [ "$do_rebuild" = "false" ] && [ -f "$_rpi_out_dir/index.html" ]; then
|
||||
for _child in "$_rpi_dir"/*; do
|
||||
[ -e "$_child" ] || continue
|
||||
if [ "$_child" -nt "$_rpi_out_dir/index.html" ]; then
|
||||
do_rebuild="true"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$do_rebuild" = "true" ]; then
|
||||
if [ "$_rpi_has_custom_index" = "true" ]; then
|
||||
parse_frontmatter "$_rpi_dir/index.md"
|
||||
else
|
||||
fm_content_warning=""
|
||||
fi
|
||||
|
||||
if [ -n "$fm_content_warning" ]; then
|
||||
content_out_file="$_rpi_out_dir/content.html"
|
||||
if [ "$_rpi_rel_dir" = "." ]; then
|
||||
content_rel_url="/content.html"
|
||||
else
|
||||
content_rel_url="/$(encode_url_path "$_rpi_rel_dir")/content.html"
|
||||
fi
|
||||
write_content_warning_outputs "$_rpi_temp_index" "$content_out_file" "$content_rel_url" "$_rpi_target_url" "$_rpi_out_dir/index.html" "$_rpi_is_home"
|
||||
else
|
||||
render_markdown "$_rpi_temp_index" "$_rpi_is_home" "$_rpi_target_url" > "$_rpi_out_dir/index.html"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ -n "$error_page" ] && [ ! -f "$out/$error_page" ]; then
|
||||
temp_404="$KEWT_TMPDIR/404_gen.md"
|
||||
echo "# 404 - Not Found" > "$temp_404"
|
||||
echo "" >> "$temp_404"
|
||||
echo "The requested page could not be found." >> "$temp_404"
|
||||
render_markdown "$temp_404" "false" "/$error_page" > "$out/$error_page"
|
||||
rm -f "$temp_404"
|
||||
fi
|
||||
build_directories() {
|
||||
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do
|
||||
rel_dir="${dir#"$src"}"
|
||||
rel_dir="${rel_dir#/}"
|
||||
[ -z "$rel_dir" ] && rel_dir="."
|
||||
out_dir="$out/$rel_dir"
|
||||
mkdir -p "$out_dir"
|
||||
|
||||
if [ -f "$dir/styles.css" ]; then
|
||||
if needs_rebuild "$dir/styles.css" "$out_dir/styles.css"; then
|
||||
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
|
||||
fi
|
||||
elif [ -f "$dir/style.css" ]; then
|
||||
if needs_rebuild "$dir/style.css" "$out_dir/styles.css"; then
|
||||
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
|
||||
fi
|
||||
fi
|
||||
|
||||
[ "$dir_indexes" != "true" ] && continue
|
||||
|
||||
build_dir_index "$dir" "$rel_dir" "$out_dir"
|
||||
done
|
||||
}
|
||||
|
||||
build_files() {
|
||||
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
|
||||
rel_path="${file#"$src"}"
|
||||
rel_path="${rel_path#/}"
|
||||
dir_rel=$(dirname "$rel_path")
|
||||
out_dir="$out/$dir_rel"
|
||||
|
||||
case "${file##*/}" in
|
||||
template.html|site.conf|style.css|styles.css|styles.root.css) continue ;;
|
||||
esac
|
||||
|
||||
if [ "${file##*/}" = "index.md" ] && grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$file" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
|
||||
is_preserved=0
|
||||
if [ -n "$(eval "find \"$file\" \( $PRESERVE_ARGS \) -print")" ]; then
|
||||
is_preserved=1
|
||||
fi
|
||||
|
||||
is_posts_dir_2="false"
|
||||
if is_posts_directory_rel "$dir_rel"; then
|
||||
is_posts_dir_2="true"
|
||||
fi
|
||||
|
||||
if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ] && [ ! -f "$(dirname "$file")/index.md" ] && [ "$is_posts_dir_2" = "false" ]; then
|
||||
load_manifest_dir_entry "$dir_rel" && [ "$dir_md_count" -eq 1 ] && continue
|
||||
fi
|
||||
|
||||
if [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ]; then
|
||||
load_manifest_entry "$rel_path" || continue
|
||||
if [ "${draft_mode:-false}" != "true" ]; then
|
||||
[ "$manifest_draft" = "true" ] && continue
|
||||
fi
|
||||
is_home="false"; [ "$file" = "$src/index.md" ] && is_home="true"
|
||||
out_file="$out/${rel_path%.md}.html"
|
||||
if needs_rebuild "$file" "$out_file"; then
|
||||
fm_title="$manifest_title"
|
||||
fm_content_warning="$manifest_content_warning"
|
||||
if [ -n "$manifest_content_warning" ]; then
|
||||
content_out_file="$out/${rel_path%.md}-content.html"
|
||||
content_rel_url="/$(encode_url_path "${rel_path%.md}")-content.html"
|
||||
orig_rel_url="$manifest_url"
|
||||
write_content_warning_outputs "$file" "$content_out_file" "$content_rel_url" "$orig_rel_url" "$out_file" "$is_home"
|
||||
else
|
||||
render_markdown "$file" "$is_home" > "$out_file"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if needs_rebuild "$file" "$out/$rel_path"; then
|
||||
cp "$file" "$out/$rel_path"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
build_root_style() {
|
||||
if [ ! -f "$src/styles.css" ] && [ ! -f "$src/style.css" ]; then
|
||||
if [ -f "$src/styles.root.css" ]; then
|
||||
_base_css="$script_dir/styles/$style.css"
|
||||
[ ! -f "$_base_css" ] && _base_css="$script_dir/styles/kewt.css"
|
||||
if [ ! -f "$out/styles.css" ] || [ "$src/styles.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
|
||||
merge_root_style "$src/styles.root.css" "$_base_css" "$out/styles.css"
|
||||
fi
|
||||
elif [ -f "$script_dir/styles/$style.css" ]; then
|
||||
if needs_rebuild "$script_dir/styles/$style.css" "$out/styles.css"; then
|
||||
copy_style_with_resolved_vars "$script_dir/styles/$style.css" "$out/styles.css"
|
||||
fi
|
||||
elif [ -f "$script_dir/styles/$style.root.css" ]; then
|
||||
_base_css="$script_dir/styles/kewt.css"
|
||||
if [ ! -f "$out/styles.css" ] || [ "$script_dir/styles/$style.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
|
||||
merge_root_style "$script_dir/styles/$style.root.css" "$_base_css" "$out/styles.css"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
build_sitemap() {
|
||||
[ -n "$base_url" ] || return
|
||||
|
||||
if [ -n "$base_url" ]; then
|
||||
sitemap_file="$out/sitemap.xml"
|
||||
base_url="${base_url%/}"
|
||||
today=$(date +%Y-%m-%d)
|
||||
@@ -407,35 +433,24 @@ if [ -n "$base_url" ]; then
|
||||
|
||||
find "$out" -type f -name "*.html" -print | sort | while IFS= read -r html_file; do
|
||||
rel_url="${html_file#"$out"}"
|
||||
|
||||
# Don't include 404 in the sitemap (duh)
|
||||
[ "${rel_url#/}" = "$error_page" ] && continue
|
||||
|
||||
{
|
||||
printf ' <url>\n'
|
||||
printf ' <loc>%s%s</loc>\n' "$base_url" "$rel_url"
|
||||
printf ' <lastmod>%s</lastmod>\n' "$today"
|
||||
printf ' </url>\n'
|
||||
} >> "$sitemap_file"
|
||||
printf ' <url>\n <loc>%s%s</loc>\n <lastmod>%s</lastmod>\n </url>\n' "$base_url" "$rel_url" "$today" >> "$sitemap_file"
|
||||
done
|
||||
|
||||
printf '</urlset>\n' >> "$sitemap_file"
|
||||
fi
|
||||
}
|
||||
|
||||
build_feed() {
|
||||
[ "$generate_feed" = "true" ] && [ -n "$base_url" ] || return
|
||||
|
||||
if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||
feed_path="$out/$feed_file"
|
||||
base_url_feed="${base_url%/}"
|
||||
build_date=$(date -u '+%a, %d %b %Y %H:%M:%S +0000')
|
||||
|
||||
printf '<?xml version="1.0" encoding="UTF-8"?>\n' > "$feed_path"
|
||||
{
|
||||
printf '<rss version="2.0">\n'
|
||||
printf ' <channel>\n'
|
||||
printf ' <title>%s</title>\n' "$title"
|
||||
printf ' <link>%s</link>\n' "$base_url_feed"
|
||||
printf ' <description>%s</description>\n' "$title"
|
||||
printf ' <lastBuildDate>%s</lastBuildDate>\n' "$build_date"
|
||||
} >> "$feed_path"
|
||||
printf '<rss version="2.0">\n <channel>\n <title>%s</title>\n <link>%s</link>\n <description>%s</description>\n <lastBuildDate>%s</lastBuildDate>\n' \
|
||||
"$title" "$base_url_feed" "$title" "$build_date" >> "$feed_path"
|
||||
|
||||
temp_feed_files="$KEWT_TMPDIR/feed_files_$$.txt"
|
||||
: > "$temp_feed_files"
|
||||
@@ -462,32 +477,26 @@ if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||
post_heading=$(echo "$post_slug" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)}1')
|
||||
fi
|
||||
feed_post_title="$post_heading - $post_date $post_time"
|
||||
|
||||
post_url="$base_url_feed$manifest_url"
|
||||
|
||||
pub_date=$(format_rfc2822_utc "$post_date" "$post_time")
|
||||
|
||||
{
|
||||
printf ' <item>\n'
|
||||
printf ' <title>%s</title>\n' "$feed_post_title"
|
||||
printf ' <link>%s</link>\n' "$post_url"
|
||||
printf ' <guid>%s</guid>\n' "$post_url"
|
||||
printf ' <pubDate>%s</pubDate>\n' "$pub_date"
|
||||
printf ' </item>\n'
|
||||
} >> "$feed_path"
|
||||
if [ "$feed_full_content" = "true" ]; then
|
||||
feed_content_file="$src/$post_rel_path"
|
||||
feed_content_html=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$feed_content_file" | sed 's/</\</g; s/>/\>/g')
|
||||
printf ' <item>\n <title>%s</title>\n <link>%s</link>\n <guid>%s</guid>\n <pubDate>%s</pubDate>\n <description>%s</description>\n </item>\n' \
|
||||
"$feed_post_title" "$post_url" "$post_url" "$pub_date" "$feed_content_html" >> "$feed_path"
|
||||
else
|
||||
printf ' <item>\n <title>%s</title>\n <link>%s</link>\n <guid>%s</guid>\n <pubDate>%s</pubDate>\n </item>\n' \
|
||||
"$feed_post_title" "$post_url" "$post_url" "$pub_date" >> "$feed_path"
|
||||
fi
|
||||
done
|
||||
|
||||
printf ' </channel>\n' >> "$feed_path"
|
||||
printf '</rss>\n' >> "$feed_path"
|
||||
fi
|
||||
printf ' </channel>\n</rss>\n' >> "$feed_path"
|
||||
}
|
||||
|
||||
if [ "$generate_search" = "true" ] || [ "$generate_tags" = "true" ]; then
|
||||
if [ "$generate_search" = "true" ]; then
|
||||
printf '[\n' > "$out/search.json"
|
||||
fi
|
||||
build_search_index() {
|
||||
printf '[\n' > "$out/search.json"
|
||||
first_search_item="true"
|
||||
temp_tags="$KEWT_TMPDIR/tags_$$.txt"
|
||||
: > "$temp_tags"
|
||||
|
||||
while IFS= read -r rel_path; do
|
||||
load_manifest_entry "$rel_path" || continue
|
||||
@@ -529,80 +538,125 @@ if [ "$generate_search" = "true" ] || [ "$generate_tags" = "true" ]; then
|
||||
[ "$manifest_draft" = "true" ] && continue
|
||||
md_heading="$manifest_title"
|
||||
|
||||
if [ "$generate_search" = "true" ]; then
|
||||
if [ -z "$manifest_content_warning" ] || [ "$include_cw_pages_in_search" = "true" ]; then
|
||||
md_content="$manifest_search_content"
|
||||
if [ -z "$manifest_content_warning" ] || [ "$include_cw_pages_in_search" = "true" ]; then
|
||||
md_content="$manifest_search_content"
|
||||
if [ "$first_search_item" = "false" ]; then
|
||||
printf ',\n' >> "$out/search.json"
|
||||
fi
|
||||
printf ' {"url": "%s", "title": "%s", "content": "%s"}' "$md_url" "$md_heading" "$md_content" >> "$out/search.json"
|
||||
first_search_item="false"
|
||||
fi
|
||||
fi
|
||||
done < "$manifest_visible_list"
|
||||
|
||||
if [ "$generate_tags" = "true" ] && [ -n "$manifest_tags" ]; then
|
||||
printf '\n]\n' >> "$out/search.json"
|
||||
cp "$script_dir/lib/search.js" "$out/search.js"
|
||||
|
||||
search_md="$KEWT_TMPDIR/search_$$.md"
|
||||
printf '%s\n' '# Search' '' \
|
||||
'<form class="kewt-search-page" action="/search.html" method="get">' \
|
||||
' <input type="text" id="search-box" name="q" placeholder="Search..." required>' \
|
||||
' <button type="submit">Search</button>' \
|
||||
'</form>' '' \
|
||||
'<div id="search-results-list">' \
|
||||
' <p>Loading...</p>' \
|
||||
'</div>' '' \
|
||||
'<script src="/search.js"></script>' > "$search_md"
|
||||
render_markdown "$search_md" "false" "/search.html" > "$out/search.html"
|
||||
rm -f "$search_md"
|
||||
}
|
||||
|
||||
build_tags() {
|
||||
temp_tags="$KEWT_TMPDIR/tags_$$.txt"
|
||||
: > "$temp_tags"
|
||||
|
||||
while IFS= read -r rel_path; do
|
||||
load_manifest_entry "$rel_path" || continue
|
||||
[ "$manifest_draft" = "true" ] && continue
|
||||
md_heading="$manifest_title"
|
||||
|
||||
if [ -n "$manifest_tags" ]; then
|
||||
old_ifs=$IFS
|
||||
IFS=','
|
||||
for tag in $manifest_tags; do
|
||||
tag=$(echo "$tag" | sed 's/^[ \t]*//;s/[ \t]*$//')
|
||||
[ -z "$tag" ] && continue
|
||||
printf '%s|%s|%s\n' "$tag" "$md_url" "$md_heading" >> "$temp_tags"
|
||||
printf '%s|%s|%s\n' "$tag" "$manifest_url" "$md_heading" >> "$temp_tags"
|
||||
done
|
||||
IFS=$old_ifs
|
||||
fi
|
||||
done < "$manifest_visible_list"
|
||||
|
||||
tags_out_dir="$out/$tags_dir"
|
||||
mkdir -p "$tags_out_dir"
|
||||
|
||||
tags_index_md="$KEWT_TMPDIR/tags_index_$$.md"
|
||||
echo "# Tags" > "$tags_index_md"
|
||||
echo "" >> "$tags_index_md"
|
||||
|
||||
cut -d'|' -f1 "$temp_tags" | sort -u | while IFS= read -r tag; do
|
||||
tag_slug=$(echo "$tag" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g')
|
||||
|
||||
echo "- [$tag](/$(echo "$tags_dir" | sed 's|^\/||; s|\/$||')/$tag_slug.html)" >> "$tags_index_md"
|
||||
|
||||
tag_page_md="$KEWT_TMPDIR/tag_page_$$.md"
|
||||
echo "# Tag: $tag" > "$tag_page_md"
|
||||
echo "" >> "$tag_page_md"
|
||||
echo "Posts tagged with **$tag**:" >> "$tag_page_md"
|
||||
echo "" >> "$tag_page_md"
|
||||
|
||||
grep "^${tag}|" "$temp_tags" | while IFS='|' read -r _t t_url t_title; do
|
||||
echo "- [$t_title]($t_url)" >> "$tag_page_md"
|
||||
done
|
||||
|
||||
render_markdown "$tag_page_md" "false" "/$tags_dir/$tag_slug.html" > "$tags_out_dir/$tag_slug.html"
|
||||
rm -f "$tag_page_md"
|
||||
done
|
||||
|
||||
render_markdown "$tags_index_md" "false" "/$tags_dir/index.html" > "$tags_out_dir/index.html"
|
||||
rm -f "$tags_index_md" "$temp_tags"
|
||||
}
|
||||
|
||||
build_error_page() {
|
||||
[ -n "$error_page" ] || return
|
||||
|
||||
error_base="${error_page%.html}"
|
||||
error_md="$src/${error_base}.md"
|
||||
|
||||
if [ -f "$error_md" ]; then
|
||||
if needs_rebuild "$error_md" "$out/$error_page"; then
|
||||
is_home="false"
|
||||
current_url="/$error_page"
|
||||
parse_frontmatter "$error_md"
|
||||
render_markdown "$error_md" "$is_home" "/$error_page" > "$out/$error_page"
|
||||
fi
|
||||
elif [ ! -f "$out/$error_page" ]; then
|
||||
temp_404="$KEWT_TMPDIR/404_gen.md"
|
||||
printf '# 404 - Not Found\n\nThe requested page could not be found.\n' > "$temp_404"
|
||||
render_markdown "$temp_404" "false" "/$error_page" > "$out/$error_page"
|
||||
rm -f "$temp_404"
|
||||
fi
|
||||
}
|
||||
|
||||
build_site() {
|
||||
echo "Building site from '$src' to '$out'..."
|
||||
|
||||
build_markdown_manifest
|
||||
build_full_nav
|
||||
|
||||
build_directories
|
||||
build_root_style
|
||||
build_files
|
||||
build_error_page
|
||||
build_sitemap
|
||||
build_feed
|
||||
|
||||
if [ "$generate_search" = "true" ]; then
|
||||
printf '\n]\n' >> "$out/search.json"
|
||||
|
||||
cp "$script_dir/lib/search.js" "$out/search.js"
|
||||
|
||||
search_md="$KEWT_TMPDIR/search_$$.md"
|
||||
printf '%s\n' '# Search' '' \
|
||||
'<form class="kewt-search-page" action="/search.html" method="get">' \
|
||||
' <input type="text" id="search-box" name="q" placeholder="Search..." required>' \
|
||||
' <button type="submit">Search</button>' \
|
||||
'</form>' '' \
|
||||
'<div id="search-results-list">' \
|
||||
' <p>Loading...</p>' \
|
||||
'</div>' '' \
|
||||
'<script src="/search.js"></script>' > "$search_md"
|
||||
render_markdown "$search_md" "false" "/search.html" > "$out/search.html"
|
||||
rm -f "$search_md"
|
||||
build_search_index
|
||||
fi
|
||||
|
||||
if [ "$generate_tags" = "true" ]; then
|
||||
tags_out_dir="$out/$tags_dir"
|
||||
mkdir -p "$tags_out_dir"
|
||||
|
||||
tags_index_md="$KEWT_TMPDIR/tags_index_$$.md"
|
||||
echo "# Tags" > "$tags_index_md"
|
||||
echo "" >> "$tags_index_md"
|
||||
|
||||
cut -d'|' -f1 "$temp_tags" | sort -u | while IFS= read -r tag; do
|
||||
tag_slug=$(echo "$tag" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g')
|
||||
|
||||
echo "- [$tag](/$(echo "$tags_dir" | sed 's|^\/||; s|\/$||')/$tag_slug.html)" >> "$tags_index_md"
|
||||
|
||||
tag_page_md="$KEWT_TMPDIR/tag_page_$$.md"
|
||||
echo "# Tag: $tag" > "$tag_page_md"
|
||||
echo "" >> "$tag_page_md"
|
||||
echo "Posts tagged with **$tag**:" >> "$tag_page_md"
|
||||
echo "" >> "$tag_page_md"
|
||||
|
||||
grep "^${tag}|" "$temp_tags" | while IFS='|' read -r _t t_url t_title; do
|
||||
echo "- [$t_title]($t_url)" >> "$tag_page_md"
|
||||
done
|
||||
|
||||
render_markdown "$tag_page_md" "false" "/$tags_dir/$tag_slug.html" > "$tags_out_dir/$tag_slug.html"
|
||||
rm -f "$tag_page_md"
|
||||
done
|
||||
|
||||
render_markdown "$tags_index_md" "false" "/$tags_dir/index.html" > "$tags_out_dir/index.html"
|
||||
rm -f "$tags_index_md"
|
||||
build_tags
|
||||
fi
|
||||
rm -f "$temp_tags"
|
||||
fi
|
||||
|
||||
echo "Build complete."
|
||||
echo "Build complete."
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2153
|
||||
|
||||
usage() {
|
||||
invoked_as=$(basename "${KEWT_INVOKED_AS:-$0}")
|
||||
@@ -21,6 +22,8 @@ Options:
|
||||
--post Create a new empty post file in the configured posts_dir with current date and time as name
|
||||
--generate-template [path] Generate a new template file at <path> (default: template.html)
|
||||
--version Show version information.
|
||||
--draft, -d Include draft pages in the build.
|
||||
--dry-run Show what would be built without writing any files.
|
||||
--from <src> Source directory (default: site)
|
||||
--to <out> Output directory (default: out)
|
||||
--watch, -w Watch for file changes and rebuild automatically.
|
||||
@@ -97,11 +100,9 @@ update_site() {
|
||||
target_conf="$update_dir/site.conf"
|
||||
target_tmpl="$update_dir/template.html"
|
||||
|
||||
# Generate default site.conf
|
||||
default_conf="$KEWT_TMPDIR/default_site.conf"
|
||||
printf '%s\n' "$DEFAULT_CONF" > "$default_conf"
|
||||
|
||||
# Update site.conf
|
||||
if [ ! -f "$target_conf" ]; then
|
||||
echo "No site.conf found in '$update_dir'; nothing to update."
|
||||
else
|
||||
@@ -126,7 +127,6 @@ update_site() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Update template.html
|
||||
if [ -f "$target_tmpl" ]; then
|
||||
default_tmpl="$KEWT_TMPDIR/default_template.html"
|
||||
printf '%s\n' "$DEFAULT_TMPL" > "$default_tmpl"
|
||||
|
||||
176
lib/config.sh
176
lib/config.sh
@@ -34,7 +34,8 @@ tags_dir = "tags"
|
||||
generate_search = false
|
||||
search_in_footer = false
|
||||
search_in_header = false
|
||||
include_cw_pages_in_search = false'
|
||||
include_cw_pages_in_search = false
|
||||
feed_full_content = false'
|
||||
|
||||
DEFAULT_TMPL='<!doctype html>
|
||||
<html lang="{{LANG}}">
|
||||
@@ -62,106 +63,89 @@ DEFAULT_TMPL='<!doctype html>
|
||||
</body>
|
||||
</html>'
|
||||
|
||||
_parse_conf_val() {
|
||||
_pv_val="$1"
|
||||
case "$_pv_val" in
|
||||
\"*\")
|
||||
_pv_val=${_pv_val#\"}
|
||||
_pv_val=${_pv_val%\"}
|
||||
printf '%s' "$_pv_val" | sed 's/\\"/\"/g; s/\\\\/\\/g'
|
||||
;;
|
||||
\'*\')
|
||||
_pv_val=${_pv_val#\'}
|
||||
_pv_val=${_pv_val%\'}
|
||||
printf '%s' "$_pv_val" | sed "s/\\\\'/'/g; s/\\\\/\\/g"
|
||||
;;
|
||||
*)
|
||||
printf '%s' "$_pv_val"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_load_conf_line() {
|
||||
case "$1" in
|
||||
''|'#'*) return ;;
|
||||
*=*) ;;
|
||||
*) return ;;
|
||||
esac
|
||||
|
||||
_lc_key=${1%%=*}
|
||||
_lc_val=${1#*=}
|
||||
_lc_key=$(printf '%s' "$_lc_key" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||
_lc_val=$(_parse_conf_val "$(printf '%s' "$_lc_val" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')")
|
||||
|
||||
case "$_lc_key" in
|
||||
title) title="$_lc_val" ;;
|
||||
style) style="${_lc_val#/}" ;;
|
||||
dir_indexes) dir_indexes="$_lc_val" ;;
|
||||
single_file_index) single_file_index="$_lc_val" ;;
|
||||
flatten) flatten="$_lc_val" ;;
|
||||
order) order="$_lc_val" ;;
|
||||
home_name) home_name="$_lc_val" ;;
|
||||
show_home_in_nav) show_home_in_nav="$_lc_val" ;;
|
||||
nav_links) nav_links="$_lc_val" ;;
|
||||
nav_extra) nav_extra="$_lc_val" ;;
|
||||
footer) footer="$_lc_val" ;;
|
||||
logo) logo="${_lc_val#/}" ;;
|
||||
display_logo) display_logo="$_lc_val" ;;
|
||||
display_title) display_title="$_lc_val" ;;
|
||||
logo_as_favicon) logo_as_favicon="$_lc_val" ;;
|
||||
favicon) favicon="${_lc_val#/}" ;;
|
||||
generate_page_title) generate_page_title="$_lc_val" ;;
|
||||
error_page) error_page="${_lc_val#/}" ;;
|
||||
versioning) versioning="$_lc_val" ;;
|
||||
enable_header_links) enable_header_links="$_lc_val" ;;
|
||||
base_url) base_url="$_lc_val" ;;
|
||||
generate_feed) generate_feed="$_lc_val" ;;
|
||||
feed_file) feed_file="${_lc_val#/}" ;;
|
||||
posts_dir) posts_dir="${_lc_val#/}" ;;
|
||||
posts_per_page) posts_per_page="$_lc_val" ;;
|
||||
custom_admonitions) custom_admonitions="$_lc_val" ;;
|
||||
cw_hide_url) cw_hide_url="$_lc_val" ;;
|
||||
lang) lang="$_lc_val" ;;
|
||||
draft_by_default) draft_by_default="$_lc_val" ;;
|
||||
generate_tags) generate_tags="$_lc_val" ;;
|
||||
tags_dir) tags_dir="${_lc_val#/}" ;;
|
||||
generate_search) generate_search="$_lc_val" ;;
|
||||
search_in_footer) search_in_footer="$_lc_val" ;;
|
||||
search_in_header) search_in_header="$_lc_val" ;;
|
||||
include_cw_pages_in_search) include_cw_pages_in_search="$_lc_val" ;;
|
||||
feed_full_content) feed_full_content="$_lc_val" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
reset_config() {
|
||||
title="kewt"
|
||||
style="kewt"
|
||||
lang="en"
|
||||
draft_by_default="false"
|
||||
dir_indexes="true"
|
||||
single_file_index="true"
|
||||
flatten="false"
|
||||
order=""
|
||||
home_name="Home"
|
||||
show_home_in_nav="true"
|
||||
nav_links=""
|
||||
nav_extra=""
|
||||
footer="made with <a href=\"https://kewt.krzak.org\">kewt</a>"
|
||||
logo=""
|
||||
display_logo="false"
|
||||
display_title="true"
|
||||
logo_as_favicon="true"
|
||||
favicon=""
|
||||
generate_page_title="true"
|
||||
error_page="not_found.html"
|
||||
versioning="false"
|
||||
enable_header_links="true"
|
||||
base_url=""
|
||||
generate_feed="false"
|
||||
feed_file="rss.xml"
|
||||
posts_dir=""
|
||||
posts_per_page="12"
|
||||
custom_admonitions=""
|
||||
cw_hide_url="true"
|
||||
generate_tags="false"
|
||||
tags_dir="tags"
|
||||
generate_search="false"
|
||||
search_in_footer="false"
|
||||
search_in_header="false"
|
||||
include_cw_pages_in_search="false"
|
||||
while IFS= read -r _rc_line; do
|
||||
_load_conf_line "$_rc_line"
|
||||
done <<EOF
|
||||
$DEFAULT_CONF
|
||||
EOF
|
||||
}
|
||||
|
||||
load_config() {
|
||||
[ -f "$1" ] || return
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
case "$line" in
|
||||
''|'#'*) continue ;;
|
||||
*=*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
|
||||
key=${line%%=*}
|
||||
val=${line#*=}
|
||||
|
||||
key=$(printf '%s' "$key" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||
val=$(printf '%s' "$val" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||
case "$val" in
|
||||
\"*\")
|
||||
val=${val#\"}; val=${val%\"}
|
||||
val=$(printf '%s' "$val" | sed 's/\\"/\"/g; s/\\\\/\\/g')
|
||||
;;
|
||||
\'*\')
|
||||
val=${val#\'}; val=${val%\'}
|
||||
val=$(printf '%s' "$val" | sed "s/\\\\'/'/g; s/\\\\/\\/g")
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$key" in
|
||||
title) title="$val" ;;
|
||||
style) style="${val#/}" ;;
|
||||
dir_indexes) dir_indexes="$val" ;;
|
||||
single_file_index) single_file_index="$val" ;;
|
||||
flatten) flatten="$val" ;;
|
||||
order) order="$val" ;;
|
||||
home_name) home_name="$val" ;;
|
||||
show_home_in_nav) show_home_in_nav="$val" ;;
|
||||
nav_links) nav_links="$val" ;;
|
||||
nav_extra) nav_extra="$val" ;;
|
||||
footer) footer="$val" ;;
|
||||
logo) logo="${val#/}" ;;
|
||||
display_logo) display_logo="$val" ;;
|
||||
display_title) display_title="$val" ;;
|
||||
logo_as_favicon) logo_as_favicon="$val" ;;
|
||||
favicon) favicon="${val#/}" ;;
|
||||
generate_page_title) generate_page_title="$val" ;;
|
||||
error_page) error_page="${val#/}" ;;
|
||||
versioning) versioning="$val" ;;
|
||||
enable_header_links) enable_header_links="$val" ;;
|
||||
base_url) base_url="$val" ;;
|
||||
generate_feed) generate_feed="$val" ;;
|
||||
feed_file) feed_file="${val#/}" ;;
|
||||
posts_dir) posts_dir="${val#/}" ;;
|
||||
posts_per_page) posts_per_page="$val" ;;
|
||||
custom_admonitions) custom_admonitions="$val" ;;
|
||||
cw_hide_url) cw_hide_url="$val" ;;
|
||||
lang) lang="$val" ;;
|
||||
draft_by_default) draft_by_default="$val" ;;
|
||||
generate_tags) generate_tags="$val" ;;
|
||||
tags_dir) tags_dir="${val#/}" ;;
|
||||
generate_search) generate_search="$val" ;;
|
||||
search_in_footer) search_in_footer="$val" ;;
|
||||
search_in_header) search_in_header="$val" ;;
|
||||
include_cw_pages_in_search) include_cw_pages_in_search="$val" ;;
|
||||
esac
|
||||
while IFS= read -r _lc_line || [ -n "$_lc_line" ]; do
|
||||
_load_conf_line "$_lc_line"
|
||||
done < "$1"
|
||||
}
|
||||
|
||||
|
||||
143
lib/generator.sh
143
lib/generator.sh
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2086
|
||||
|
||||
SEARCH_FORM_FOOTER='<form class="kewt-search-footer" action="/search.html" method="get"><input type="text" name="q" placeholder="Search..." required><button type="submit">Go</button></form>'
|
||||
SEARCH_FORM_HEADER='<form class="kewt-search-header" action="/search.html" method="get"><input type="text" name="q" placeholder="Search..." required><button type="submit">Go</button></form>'
|
||||
@@ -37,12 +38,14 @@ generate_nav() {
|
||||
|
||||
sort -u "$nav_input" | AWK_SRC="$1" AWK_SINGLE_FILE_INDEX="$single_file_index" AWK_FLATTEN="$flatten" AWK_ORDER="$order" AWK_HOME_NAME="$home_name" AWK_SHOW_HOME_IN_NAV="$show_home_in_nav" AWK_DINFO="$dinfo" awk -f "$awk_dir/generate_sidebar.awk"
|
||||
}
|
||||
|
||||
escape_html_text() {
|
||||
printf '%s' "$1" | sed \
|
||||
-e 's/&/\&/g' \
|
||||
-e 's/</\</g' \
|
||||
-e 's/>/\>/g'
|
||||
}
|
||||
|
||||
escape_html_attr() {
|
||||
printf '%s' "$1" | sed \
|
||||
-e 's/&/\&/g' \
|
||||
@@ -50,13 +53,13 @@ escape_html_attr() {
|
||||
-e 's/</\</g' \
|
||||
-e 's/>/\>/g'
|
||||
}
|
||||
|
||||
nav_links_html() {
|
||||
[ -n "$nav_links" ] || return
|
||||
|
||||
old_ifs=$IFS
|
||||
set -f
|
||||
IFS=','
|
||||
# shellcheck disable=SC2086
|
||||
set -- $nav_links
|
||||
IFS=$old_ifs
|
||||
set +f
|
||||
@@ -93,6 +96,7 @@ nav_links_html() {
|
||||
done
|
||||
printf '</ul>'
|
||||
}
|
||||
|
||||
find_closest() {
|
||||
target="$1"
|
||||
start_dir="$2"
|
||||
@@ -108,11 +112,13 @@ find_closest() {
|
||||
echo "$src/$target"
|
||||
fi
|
||||
}
|
||||
|
||||
copy_style_with_resolved_vars() {
|
||||
src_style="$1"
|
||||
out_style="$2"
|
||||
awk -f "$awk_dir/replace_variables.awk" "$src_style" > "$out_style"
|
||||
}
|
||||
|
||||
merge_root_style() {
|
||||
root_file="$1"
|
||||
base_css="$2"
|
||||
@@ -134,51 +140,15 @@ merge_root_style() {
|
||||
' "$base_css"
|
||||
} | awk -f "$awk_dir/replace_variables.awk" > "$out_file"
|
||||
}
|
||||
render_markdown() {
|
||||
file="$1"
|
||||
is_home="$2"
|
||||
url_override="$3"
|
||||
|
||||
if [ -n "$url_override" ]; then
|
||||
current_url="$url_override"
|
||||
else
|
||||
rel_path="${file#"$src"}"
|
||||
rel_path="${rel_path#/}"
|
||||
current_url=$(markdown_file_url "$rel_path")
|
||||
fi
|
||||
|
||||
content_file="$file"
|
||||
if [ -n "$posts_dir" ] && [ "$file" != "$src/$posts_dir/index.md" ]; then
|
||||
rel_dir_of_url=$(dirname "$current_url")
|
||||
rel_dir_of_url="${rel_dir_of_url#/}"
|
||||
if { [ "$rel_dir_of_url" = "$posts_dir" ] || [ "./$rel_dir_of_url" = "$posts_dir" ]; } && [ "$(basename "$current_url")" != "index.html" ]; then
|
||||
temp_post_with_backlink="$KEWT_TMPDIR/post_with_backlink_$$.md"
|
||||
printf "[< Back](index.html)\n\n" > "$temp_post_with_backlink"
|
||||
awk -f "$awk_dir/frontmatter.awk" "$file" >> "$temp_post_with_backlink"
|
||||
|
||||
post_md_name="$(basename "$current_url" .html).md"
|
||||
prevnext_file="$KEWT_TMPDIR/prevnext/$post_md_name"
|
||||
if [ -f "$prevnext_file" ]; then
|
||||
IFS='|' read -r prev_str next_str < "$prevnext_file"
|
||||
|
||||
printf "\n\n---\n<div class=\"post-nav\">\n" >> "$temp_post_with_backlink"
|
||||
if [ -n "$prev_str" ]; then
|
||||
printf "<span class=\"prev-post\">%s</span>\n" "$prev_str" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
if [ -n "$next_str" ]; then
|
||||
printf "<span class=\"next-post\">%s</span>\n" "$next_str" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
printf "</div>\n" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
content_file="$temp_post_with_backlink"
|
||||
fi
|
||||
fi
|
||||
|
||||
local_template=$(find_closest "template.html" "$(dirname "$file")")
|
||||
resolve_render_template() {
|
||||
local_template=$(find_closest "template.html" "$(dirname "$1")")
|
||||
[ -z "$local_template" ] && local_template="$template"
|
||||
}
|
||||
|
||||
closest_style_src=$(find_closest "styles.css" "$(dirname "$file")")
|
||||
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")")
|
||||
resolve_render_style_path() {
|
||||
closest_style_src=$(find_closest "styles.css" "$(dirname "$1")")
|
||||
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$1")")
|
||||
if [ -n "$closest_style_src" ]; then
|
||||
style_rel_to_src="${closest_style_src#"$src"/}"
|
||||
case "$closest_style_src" in
|
||||
@@ -190,7 +160,9 @@ render_markdown() {
|
||||
else
|
||||
style_path="/styles.css"
|
||||
fi
|
||||
}
|
||||
|
||||
build_header_brand_html() {
|
||||
logo_html=""
|
||||
if [ "$display_logo" = "true" ] && [ -n "$logo" ]; then
|
||||
logo_html="<img class=\"site-logo\" src=\"$logo\" alt=\"$title\" />"
|
||||
@@ -210,26 +182,27 @@ render_markdown() {
|
||||
else
|
||||
header_brand="<a href=\"/index.html\">$title</a>"
|
||||
fi
|
||||
}
|
||||
|
||||
build_favicon_head() {
|
||||
favicon_src=""
|
||||
if [ "$logo_as_favicon" = "true" ] && [ -n "$logo" ]; then
|
||||
favicon_src="$logo"
|
||||
elif [ -n "$favicon" ]; then
|
||||
favicon_src="$favicon"
|
||||
fi
|
||||
head_extra=""
|
||||
|
||||
if [ -n "$favicon_src" ]; then
|
||||
if echo "$favicon_src" | grep -q "^http"; then
|
||||
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||
elif echo "$favicon_src" | grep -q "^/"; then
|
||||
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||
else
|
||||
head_extra="<link rel=\"icon\" href=\"/$favicon_src\" />"
|
||||
fi
|
||||
case "$favicon_src" in
|
||||
http*|/*) head_extra="<link rel=\"icon\" href=\"$favicon_src\" />" ;;
|
||||
*) head_extra="<link rel=\"icon\" href=\"/$favicon_src\" />" ;;
|
||||
esac
|
||||
else
|
||||
head_extra=""
|
||||
fi
|
||||
}
|
||||
|
||||
parse_frontmatter "$file"
|
||||
|
||||
build_page_title() {
|
||||
page_title="$title"
|
||||
if [ -n "$fm_title" ]; then
|
||||
page_title="$fm_title - $title"
|
||||
@@ -250,7 +223,9 @@ render_markdown() {
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
build_og_tags() {
|
||||
head_extra_og="<meta property=\"og:title\" content=\"$(escape_html_attr "$page_title")\" />"
|
||||
if [ -n "$fm_description" ]; then
|
||||
head_extra_og="$head_extra_og
|
||||
@@ -266,17 +241,23 @@ render_markdown() {
|
||||
else
|
||||
head_extra="$head_extra_og"
|
||||
fi
|
||||
}
|
||||
|
||||
build_cw_url_hide() {
|
||||
if [ "$is_cw_content_page" = "true" ] && [ "$cw_hide_url" = "true" ]; then
|
||||
head_extra="$head_extra
|
||||
<script>window.history.replaceState(null, '', '$current_url');</script>"
|
||||
fi
|
||||
}
|
||||
|
||||
build_composed_footer() {
|
||||
final_footer="$footer"
|
||||
if [ "$search_in_footer" = "true" ]; then
|
||||
final_footer="$footer $SEARCH_FORM_FOOTER"
|
||||
fi
|
||||
}
|
||||
|
||||
build_composed_nav() {
|
||||
final_nav="$nav"
|
||||
final_header_brand="$header_brand"
|
||||
final_header_search=""
|
||||
@@ -285,9 +266,65 @@ render_markdown() {
|
||||
final_nav="$SEARCH_FORM_NAV
|
||||
$nav"
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_post_content() {
|
||||
content_file="$file"
|
||||
if [ -n "$posts_dir" ] && [ "$file" != "$src/$posts_dir/index.md" ]; then
|
||||
rel_dir_of_url=$(dirname "$current_url")
|
||||
rel_dir_of_url="${rel_dir_of_url#/}"
|
||||
if { [ "$rel_dir_of_url" = "$posts_dir" ] || [ "./$rel_dir_of_url" = "$posts_dir" ]; } && [ "$(basename "$current_url")" != "index.html" ]; then
|
||||
temp_post_with_backlink="$KEWT_TMPDIR/post_with_backlink_$$.md"
|
||||
printf "[< Back](index.html)\n\n" > "$temp_post_with_backlink"
|
||||
awk -f "$awk_dir/frontmatter.awk" "$file" >> "$temp_post_with_backlink"
|
||||
|
||||
post_md_name="$(basename "$current_url" .html).md"
|
||||
prevnext_file="$KEWT_TMPDIR/prevnext/$post_md_name"
|
||||
if [ -f "$prevnext_file" ]; then
|
||||
IFS='|' read -r prev_str next_str < "$prevnext_file"
|
||||
|
||||
printf "\n\n---\n<div class=\"post-nav\">\n" >> "$temp_post_with_backlink"
|
||||
if [ -n "$prev_str" ]; then
|
||||
printf "<span class=\"prev-post\">%s</span>\n" "$prev_str" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
if [ -n "$next_str" ]; then
|
||||
printf "<span class=\"next-post\">%s</span>\n" "$next_str" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
printf "</div>\n" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
content_file="$temp_post_with_backlink"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
render_markdown() {
|
||||
file="$1"
|
||||
is_home="$2"
|
||||
url_override="$3"
|
||||
|
||||
if [ -n "$url_override" ]; then
|
||||
current_url="$url_override"
|
||||
else
|
||||
rel_path="${file#"$src"}"
|
||||
rel_path="${rel_path#/}"
|
||||
current_url=$(markdown_file_url "$rel_path")
|
||||
fi
|
||||
|
||||
prepare_post_content
|
||||
resolve_render_template "$file"
|
||||
resolve_render_style_path "$file"
|
||||
build_header_brand_html
|
||||
build_favicon_head
|
||||
parse_frontmatter "$file"
|
||||
build_page_title
|
||||
build_og_tags
|
||||
build_cw_url_hide
|
||||
build_composed_footer
|
||||
build_composed_nav
|
||||
|
||||
ENABLE_HEADER_LINKS="$enable_header_links" CUSTOM_ADMONITIONS="$custom_admonitions" MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$content_file" | AWK_LANG="$lang" AWK_CURRENT_URL="$current_url" AWK_TITLE="$page_title" AWK_NAV="$final_nav" AWK_FOOTER="$final_footer" AWK_STYLE_PATH="${style_path}" AWK_HEADER_BRAND="$final_header_brand" AWK_HEADER_SEARCH="$final_header_search" AWK_HEAD_EXTRA="$head_extra" AWK_VERSION="$asset_version" AWK_CONTENT_WARNING="$fm_content_warning" awk -f "$awk_dir/render_template.awk" "$local_template"
|
||||
}
|
||||
|
||||
generate_content_warning_page() {
|
||||
_fm_title="$1"
|
||||
_fm_content_warning="$2"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2016,SC2030,SC2031
|
||||
|
||||
shell_quote() {
|
||||
printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"
|
||||
@@ -186,8 +187,10 @@ build_markdown_manifest() {
|
||||
eval "find \"$src\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print" | sort | while IFS= read -r visible_file; do
|
||||
visible_rel_path="${visible_file#"$src"/}"
|
||||
load_manifest_entry "$visible_rel_path" || continue
|
||||
[ "$manifest_draft" = "true" ] && continue
|
||||
manifest_dir_hidden_by_draft_index "$manifest_dir_rel" && continue
|
||||
if [ "${draft_mode:-false}" != "true" ]; then
|
||||
[ "$manifest_draft" = "true" ] && continue
|
||||
manifest_dir_hidden_by_draft_index "$manifest_dir_rel" && continue
|
||||
fi
|
||||
printf '%s\n' "$visible_rel_path" >> "$manifest_visible_list"
|
||||
done
|
||||
}
|
||||
|
||||
79
markdown.sh
79
markdown.sh
@@ -3,14 +3,11 @@
|
||||
script_dir=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
|
||||
awk_dir="$script_dir/awk"
|
||||
|
||||
sed_inplace() {
|
||||
script="$1"
|
||||
file="$2"
|
||||
tmp="${file}.tmp.$$"
|
||||
if sed "$script" "$file" > "$tmp" && mv "$tmp" "$file"; then
|
||||
return 0
|
||||
else
|
||||
rm -f "$tmp"
|
||||
run_awk() {
|
||||
_ra_awk_file="$1"
|
||||
shift
|
||||
if ! awk -f "$_ra_awk_file" "$@"; then
|
||||
echo "Error: AWK failed: $_ra_awk_file" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -21,36 +18,20 @@ cat "$@" > "$temp_file"
|
||||
|
||||
trap 'rm -f "$temp_file" "$temp_file.tmp" "$temp_file.fm"' EXIT INT TERM
|
||||
|
||||
# Frontmatter
|
||||
fm_file="$temp_file.fm"
|
||||
: > "$fm_file"
|
||||
awk -v fm_out="$fm_file" -f "$awk_dir/frontmatter.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
|
||||
# Mask
|
||||
awk -f "$awk_dir/mask_inline_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/mask_plain.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
run_awk "$awk_dir/frontmatter.awk" -v fm_out="$fm_file" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
|
||||
# Reference links
|
||||
refs=$(awk '/^\[[^\]]+\]: */' "$temp_file")
|
||||
IFS='
|
||||
'
|
||||
for ref in $refs; do
|
||||
ref_id=$(echo "$ref" | sed 's/^\[\(.*\)\]: .*/\1/')
|
||||
ref_url=$(echo "$ref" | sed 's/^\[.*\]: \([^ ]*\).*/\1/')
|
||||
ref_title=$(echo "$ref" | sed -n 's/^\[.*\]: [^ ]* "\(.*\)"/\1/p' | sed 's@|@!@g')
|
||||
sed_inplace "s|!\[\([^]]*\)\]\[$ref_id\]|<img src=\"$ref_url\" title=\"$ref_title\" alt=\"\1\" />|g" "$temp_file"
|
||||
sed_inplace "s|\[\([^]]*\)\]\[$ref_id\]|<a href=\"$ref_url\" title=\"$ref_title\">\1</a>|g" "$temp_file"
|
||||
sed_inplace "s|!\[$ref_id\]\[\]|<img src=\"$ref_url\" title=\"$ref_title\" alt=\"$ref_id\" />|g" "$temp_file"
|
||||
sed_inplace "s|\[$ref_id\]\[\]|<a href=\"$ref_url\" title=\"$ref_title\">$ref_id</a>|g" "$temp_file"
|
||||
done
|
||||
sed_inplace "/^\[[^\]]*\]: */d" "$temp_file"
|
||||
|
||||
# Blocks
|
||||
run_awk "$awk_dir/mask_inline_code.awk" "$temp_file" \
|
||||
| run_awk "$awk_dir/mask_plain.awk" \
|
||||
| run_awk "$awk_dir/reference_links.awk" \
|
||||
> "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
|
||||
loop_count=0
|
||||
max_iterations=100
|
||||
while grep '^>' "$temp_file" >/dev/null; do
|
||||
awk -f "$awk_dir/blockquote.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
while grep -q '^>' "$temp_file"; do
|
||||
run_awk "$awk_dir/blockquote.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
loop_count=$((loop_count + 1))
|
||||
if [ "$loop_count" -gt "$max_iterations" ]; then
|
||||
echo "Warning: Blockquote processing exceeded $max_iterations iterations on $1. Breaking to prevent infinite loop." >&2
|
||||
@@ -58,25 +39,19 @@ while grep '^>' "$temp_file" >/dev/null; do
|
||||
fi
|
||||
done
|
||||
|
||||
awk -v custom_admonitions="$CUSTOM_ADMONITIONS" -f "$awk_dir/blockquote_to_admonition.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/fenced_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/indented_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/pipe_tables.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -v enable_header_links="$ENABLE_HEADER_LINKS" -f "$awk_dir/headers.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/definition_lists.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/lists.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
run_awk "$awk_dir/blockquote_to_admonition.awk" -v custom_admonitions="$CUSTOM_ADMONITIONS" "$temp_file" \
|
||||
| run_awk "$awk_dir/fenced_code.awk" \
|
||||
| run_awk "$awk_dir/indented_code.awk" \
|
||||
| run_awk "$awk_dir/pipe_tables.awk" \
|
||||
| run_awk "$awk_dir/definition_lists.awk" \
|
||||
| run_awk "$awk_dir/lists.awk" \
|
||||
| run_awk "$awk_dir/toc.awk" \
|
||||
| run_awk "$awk_dir/footnotes.awk" \
|
||||
| run_awk "$awk_dir/breaks.awk" \
|
||||
| run_awk "$awk_dir/paragraphs.awk" \
|
||||
| run_awk "$awk_dir/emoji.awk" -v emoji_file="$awk_dir/emoji.tsv" \
|
||||
| run_awk "$awk_dir/markdown_inline.awk" \
|
||||
| run_awk "$awk_dir/headers.awk" -v enable_header_links="$ENABLE_HEADER_LINKS" \
|
||||
| run_awk "$awk_dir/markdown_embed.awk" -v input_file="$1" -v site_root="$MARKDOWN_SITE_ROOT" -v fallback_file="$MARKDOWN_FALLBACK_FILE" -v script_dir="$script_dir"
|
||||
|
||||
# TOC
|
||||
awk -f "$awk_dir/toc.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
# Footnotes
|
||||
awk -f "$awk_dir/footnotes.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
|
||||
# Spacing
|
||||
awk -f "$awk_dir/breaks.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/paragraphs.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
|
||||
# Inline styles
|
||||
awk -v emoji_file="$awk_dir/emoji.tsv" -f "$awk_dir/emoji.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/markdown_inline.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -v input_file="$1" -v site_root="$MARKDOWN_SITE_ROOT" -v fallback_file="$MARKDOWN_FALLBACK_FILE" -v script_dir="$script_dir" -f "$awk_dir/markdown_embed.awk" "$temp_file"
|
||||
rm "$temp_file"
|
||||
rm -f "$temp_file"
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
pkgbase = kewt-git
|
||||
pkgdesc = A minimalist, 100% POSIX, static site generator inspired by werc and kew
|
||||
pkgver = r0.0000000
|
||||
pkgrel = 3
|
||||
pkgver = r0.0000002
|
||||
pkgrel = 1
|
||||
url = https://kewt.krzak.org
|
||||
arch = any
|
||||
license = ISC
|
||||
makedepends = git
|
||||
depends = sh
|
||||
optdepends = bash-completion: bash shell completions
|
||||
provides = kewt
|
||||
conflicts = kewt
|
||||
conflicts = kewt-bin
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
pkgbase = kewt-bin
|
||||
pkgdesc = A minimalist, 100% POSIX, static site generator inspired by werc and kew
|
||||
pkgver = VERSION_PLACEHOLDER
|
||||
pkgrel = 2
|
||||
pkgrel = 1
|
||||
url = https://kewt.krzak.org
|
||||
arch = any
|
||||
license = ISC
|
||||
depends = sh
|
||||
optdepends = bash-completion: bash shell completions
|
||||
provides = kewt
|
||||
conflicts = kewt
|
||||
conflicts = kewt-git
|
||||
source = kewt-bin-VERSION_PLACEHOLDER.sh::https://git.krzak.org/N0VA/kewt/releases/download/vVERSION_PLACEHOLDER/kewt
|
||||
source = kewt-bin-VERSION_PLACEHOLDER.bash::https://git.krzak.org/N0VA/kewt/releases/download/vVERSION_PLACEHOLDER/kewt.bash
|
||||
sha256sums = SHA256SUM_PLACEHOLDER
|
||||
sha256sums = SKIP
|
||||
|
||||
pkgname = kewt-bin
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Maintainer: n0va <n0va@krzak.org>
|
||||
pkgname=kewt-git
|
||||
pkgver=r0.0000000
|
||||
pkgrel=2
|
||||
pkgver=r0.0000002
|
||||
pkgrel=1
|
||||
pkgdesc="A minimalist, 100% POSIX, static site generator inspired by werc and kew"
|
||||
arch=('any')
|
||||
url="https://kewt.krzak.org"
|
||||
@@ -28,4 +28,5 @@ package() {
|
||||
install -Dm755 kewt "${pkgdir}/usr/bin/kewt"
|
||||
install -d "${pkgdir}/usr/share/zsh/site-functions"
|
||||
"${pkgdir}/usr/bin/kewt" --dump-zsh-completions > "${pkgdir}/usr/share/zsh/site-functions/_kewt"
|
||||
install -Dm644 packaging/bash/kewt.bash "${pkgdir}/usr/share/bash-completion/completions/kewt"
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ license=('ISC')
|
||||
depends=('sh')
|
||||
provides=('kewt')
|
||||
conflicts=('kewt' 'kewt-git')
|
||||
source=("${pkgname}-${pkgver}.sh::https://git.krzak.org/N0VA/kewt/releases/download/v${pkgver}/kewt")
|
||||
sha256sums=('SHA256SUM_PLACEHOLDER')
|
||||
source=("${pkgname}-${pkgver}.sh::https://git.krzak.org/N0VA/kewt/releases/download/v${pkgver}/kewt"
|
||||
"${pkgname}-${pkgver}.bash::https://git.krzak.org/N0VA/kewt/releases/download/v${pkgver}/kewt.bash")
|
||||
sha256sums=('SHA256SUM_PLACEHOLDER' 'SKIP')
|
||||
|
||||
build() {
|
||||
chmod +x "${srcdir}/${pkgname}-${pkgver}.sh"
|
||||
@@ -20,4 +21,5 @@ package() {
|
||||
install -Dm755 "${srcdir}/${pkgname}-${pkgver}.sh" "${pkgdir}/usr/bin/kewt"
|
||||
install -d "${pkgdir}/usr/share/zsh/site-functions"
|
||||
"${pkgdir}/usr/bin/kewt" --dump-zsh-completions > "${pkgdir}/usr/share/zsh/site-functions/_kewt"
|
||||
install -Dm644 "${srcdir}/${pkgname}-${pkgver}.bash" "${pkgdir}/usr/share/bash-completion/completions/kewt"
|
||||
}
|
||||
|
||||
37
packaging/bash/kewt.bash
Normal file
37
packaging/bash/kewt.bash
Normal file
@@ -0,0 +1,37 @@
|
||||
_kewt() {
|
||||
local cur prev opts
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
opts="--help --new --init --clean --no-clean --update --post --generate-template --version --from --to --watch -w --serve -s --draft --dry-run"
|
||||
|
||||
case "$prev" in
|
||||
--from|--to)
|
||||
COMPREPLY=$(compgen -d -- "$cur")
|
||||
return 0
|
||||
;;
|
||||
--serve|-s)
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
;;
|
||||
--new|--init|--update)
|
||||
COMPREPLY=$(compgen -d -- "$cur")
|
||||
return 0
|
||||
;;
|
||||
--generate-template)
|
||||
COMPREPLY=$(compgen -f -- "$cur")
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=$(compgen -W "$opts" -- "$cur")
|
||||
return 0
|
||||
fi
|
||||
|
||||
COMPREPLY=$(compgen -d -- "$cur")
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F _kewt kewt
|
||||
41
packaging/fedora/kewt.spec.template
Normal file
41
packaging/fedora/kewt.spec.template
Normal file
@@ -0,0 +1,41 @@
|
||||
Name: kewt
|
||||
Version: VERSION_PLACEHOLDER
|
||||
Release: 1%{?dist}
|
||||
Summary: A minimalist, 100% POSIX, static site generator inspired by werc
|
||||
|
||||
License: ISC
|
||||
URL: https://kewt.krzak.org
|
||||
Source0: https://git.krzak.org/N0VA/kewt/archive/v%{version}.tar.gz
|
||||
|
||||
BuildArch: noarch
|
||||
BuildRequires: make
|
||||
Requires: sh
|
||||
Requires: findutils
|
||||
Requires: grep
|
||||
Requires: sed
|
||||
Requires: gawk
|
||||
Recommends: python3
|
||||
Recommends: bash-completion
|
||||
|
||||
%description
|
||||
A minimalist, 100% POSIX, static site generator inspired by werc and kew
|
||||
|
||||
%prep
|
||||
%autosetup
|
||||
|
||||
%build
|
||||
%make_build
|
||||
|
||||
%install
|
||||
%make_install PREFIX=%{_prefix} BINDIR=%{_bindir} ZSHCOMPDIR=%{_datadir}/zsh/site-functions BASHCOMPDIR=%{_datadir}/bash-completion/completions
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md
|
||||
%{_bindir}/kewt
|
||||
%{_datadir}/zsh/site-functions/_kewt
|
||||
%{_datadir}/bash-completion/completions/kewt
|
||||
|
||||
%changelog
|
||||
* Mon May 20 2024 n0va <n0va@krzak.org> - VERSION_PLACEHOLDER-1
|
||||
- Initial package for Fedora
|
||||
@@ -8,7 +8,8 @@ class Kewt < Formula
|
||||
|
||||
def install
|
||||
bin.install "kewt"
|
||||
generate_completions_from_executable(bin/"kewt", "--dump-zsh-completions", shells: [:zsh])
|
||||
chmod 0755, bin/"kewt"
|
||||
generate_completions_from_executable(bin/"kewt", "--dump-zsh-completions", shells: [:zsh, :bash])
|
||||
end
|
||||
|
||||
test do
|
||||
|
||||
@@ -17,6 +17,8 @@ _kewt() {
|
||||
'--to[Output directory]:directory:_directories'
|
||||
'(-w --watch)'{-w,--watch}'[Watch for file changes and rebuild automatically]'
|
||||
'(-s --serve)'{-s,--serve}'[Start a local HTTP server after building]::port:'
|
||||
'(-d --draft)'{-d,--draft}'[Include draft pages in the build]'
|
||||
'(-)--dry-run[Show what would be built without writing any files]'
|
||||
)
|
||||
|
||||
_arguments -S -C $args '*: :_directories'
|
||||
|
||||
@@ -38,6 +38,14 @@ sudo make install
|
||||
brew tap n0va-bot/tap
|
||||
brew install kewt
|
||||
```
|
||||
|
||||
### Fedora
|
||||
|
||||
```sh
|
||||
sudo dnf copr enable n0va-bot/kewt
|
||||
sudo dnf install kewt
|
||||
```
|
||||
|
||||
### bpkg
|
||||
|
||||
```sh
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
---
|
||||
title = "Heaven"
|
||||
content_warning = "This page may contain CSS"
|
||||
content_warning = "This page may have bad effects on people with an allergy to cats"
|
||||
---
|
||||
# Heaven
|
||||
|
||||
Told you
|
||||
|
||||
Probably should've mentioned the catgirl too
|
||||
|
||||
| --- | --- |
|
||||
| ```!![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |
|
||||
![catgirl.jpg]
|
||||
|
||||
@@ -37,6 +37,11 @@ body {
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
padding: 20px;
|
||||
padding-bottom: 0;
|
||||
border-bottom: 1px solid var(--code-border);
|
||||
@@ -45,6 +50,8 @@ header {
|
||||
|
||||
header h1 {
|
||||
margin: 0;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
font-size: 35px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
@@ -114,6 +121,12 @@ a {
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
#side-bar a.current-page:hover,
|
||||
#side-bar a.current-page:focus {
|
||||
color: var(--fg);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
@@ -131,6 +144,53 @@ h3 {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.header-link {
|
||||
color: var(--fg);
|
||||
display: inline;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--fg-muted);
|
||||
}
|
||||
|
||||
.header-link:hover,
|
||||
.header-link:focus {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
text-decoration-color: var(--fg);
|
||||
}
|
||||
|
||||
.header-anchor {
|
||||
opacity: 0;
|
||||
margin-left: 0.25em;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
h1:hover > .header-anchor,
|
||||
h2:hover > .header-anchor,
|
||||
h3:hover > .header-anchor,
|
||||
h4:hover > .header-anchor,
|
||||
h5:hover > .header-anchor,
|
||||
h6:hover > .header-anchor,
|
||||
h1:focus-within > .header-anchor,
|
||||
h2:focus-within > .header-anchor,
|
||||
h3:focus-within > .header-anchor,
|
||||
h4:focus-within > .header-anchor,
|
||||
h5:focus-within > .header-anchor,
|
||||
h6:focus-within > .header-anchor,
|
||||
h1:hover .header-link .header-anchor,
|
||||
h2:hover .header-link .header-anchor,
|
||||
h3:hover .header-link .header-anchor,
|
||||
h4:hover .header-link .header-anchor,
|
||||
h5:hover .header-link .header-anchor,
|
||||
h6:hover .header-link .header-anchor,
|
||||
h1:focus-within .header-link .header-anchor,
|
||||
h2:focus-within .header-link .header-anchor,
|
||||
h3:focus-within .header-link .header-anchor,
|
||||
h4:focus-within .header-link .header-anchor,
|
||||
h5:focus-within .header-link .header-anchor,
|
||||
h6:focus-within .header-link .header-anchor {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: var(--code-bg);
|
||||
color: var(--code-fg);
|
||||
@@ -455,9 +515,10 @@ tr:nth-child(even) {
|
||||
.kewt-search-header {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
margin-left: auto;
|
||||
margin-left: 0;
|
||||
vertical-align: middle;
|
||||
font-style: normal;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.kewt-search-header input[type="text"] {
|
||||
|
||||
185
tests/test_builder.sh
Normal file
185
tests/test_builder.sh
Normal file
@@ -0,0 +1,185 @@
|
||||
test_needs_rebuild_no_output() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
. "$project_dir/lib/builder.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
echo "test" > "$tmpdir/src.md"
|
||||
|
||||
needs_rebuild "$tmpdir/src.md" "$tmpdir/out.html"
|
||||
result=$?
|
||||
assert_eq "0" "$result" "rebuild when output missing"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_needs_rebuild_output_newer() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
. "$project_dir/lib/builder.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
echo "test" > "$tmpdir/src.md"
|
||||
sleep 1
|
||||
echo "test" > "$tmpdir/out.html"
|
||||
|
||||
needs_rebuild "$tmpdir/src.md" "$tmpdir/out.html"
|
||||
result=$?
|
||||
assert_eq "1" "$result" "no rebuild when output newer"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_needs_rebuild_source_newer() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
. "$project_dir/lib/builder.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
echo "old" > "$tmpdir/out.html"
|
||||
sleep 1
|
||||
echo "new" > "$tmpdir/src.md"
|
||||
|
||||
needs_rebuild "$tmpdir/src.md" "$tmpdir/out.html"
|
||||
result=$?
|
||||
assert_eq "0" "$result" "rebuild when source newer"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_escape_html_text() {
|
||||
. "$project_dir/lib/generator.sh"
|
||||
|
||||
result=$(escape_html_text "<script>&test>")
|
||||
assert_eq "<script>&test>" "$result" "escape html text"
|
||||
}
|
||||
|
||||
test_escape_html_attr() {
|
||||
. "$project_dir/lib/generator.sh"
|
||||
|
||||
result=$(escape_html_attr 'value with "quotes" & <tags>')
|
||||
assert_eq "value with "quotes" & <tags>" "$result" "escape html attr"
|
||||
}
|
||||
|
||||
test_nav_links_empty() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/generator.sh"
|
||||
|
||||
nav_links=""
|
||||
result=$(nav_links_html)
|
||||
assert_eq "" "$result" "empty nav links"
|
||||
}
|
||||
|
||||
test_nav_links_markdown() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/generator.sh"
|
||||
|
||||
nav_links="[Docs](/docs), [About](/about)"
|
||||
result=$(nav_links_html)
|
||||
assert_contains '<li><a href="/docs">Docs</a></li>' "$result" "nav links markdown docs"
|
||||
assert_contains '<li><a href="/about">About</a></li>' "$result" "nav links markdown about"
|
||||
}
|
||||
|
||||
test_nav_links_plain() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/generator.sh"
|
||||
|
||||
nav_links="https://example.com"
|
||||
result=$(nav_links_html)
|
||||
assert_contains '<li><a href="https://example.com">' "$result" "nav links plain url"
|
||||
}
|
||||
|
||||
test_find_closest() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/generator.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir/sub/deep"
|
||||
echo "root" > "$tmpdir/template.html"
|
||||
echo "sub" > "$tmpdir/sub/template.html"
|
||||
|
||||
src="$tmpdir"
|
||||
result=$(find_closest "template.html" "$tmpdir/sub/deep")
|
||||
assert_eq "$tmpdir/sub/template.html" "$result" "find closest in parent"
|
||||
|
||||
result=$(find_closest "template.html" "$tmpdir")
|
||||
assert_eq "$tmpdir/template.html" "$result" "find closest in current"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_find_closest_fallback_to_src() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/generator.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir/sub"
|
||||
echo "root" > "$tmpdir/template.html"
|
||||
|
||||
src="$tmpdir"
|
||||
result=$(find_closest "template.html" "$tmpdir/sub")
|
||||
assert_eq "$tmpdir/template.html" "$result" "find closest falls back to src"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_custom_404_md() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
. "$project_dir/lib/manifest.sh"
|
||||
. "$project_dir/lib/generator.sh"
|
||||
. "$project_dir/lib/builder.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
|
||||
cat > "$tmpdir/site.conf" <<EOF
|
||||
title = "Test"
|
||||
error_page = "not_found.html"
|
||||
EOF
|
||||
|
||||
printf '# Custom 404\n\nPage not found, sorry.\n' > "$tmpdir/not_found.md"
|
||||
|
||||
src="$tmpdir"
|
||||
out="$tmpdir/out"
|
||||
KEWT_TMPDIR="$tmpdir/tmp"
|
||||
mkdir -p "$out" "$KEWT_TMPDIR"
|
||||
|
||||
awk_dir="$project_dir/awk"
|
||||
script_dir="$project_dir"
|
||||
style="kewt"
|
||||
template="$KEWT_TMPDIR/default_template.html"
|
||||
printf '%s\n' "$DEFAULT_TMPL" > "$template"
|
||||
nav=""
|
||||
footer=""
|
||||
header_brand=""
|
||||
head_extra=""
|
||||
asset_version=""
|
||||
lang="en"
|
||||
current_url=""
|
||||
fm_title=""
|
||||
fm_content_warning=""
|
||||
fm_description=""
|
||||
generate_page_title="true"
|
||||
logo_as_favicon="false"
|
||||
favicon=""
|
||||
display_logo="false"
|
||||
display_title="true"
|
||||
logo=""
|
||||
search_in_header="false"
|
||||
search_in_footer="false"
|
||||
cw_hide_url="true"
|
||||
enable_header_links="false"
|
||||
custom_admonitions=""
|
||||
|
||||
build_error_page
|
||||
|
||||
assert_file_exists "$out/not_found.html" "custom 404 html generated"
|
||||
assert_contains "Custom 404" "$(cat "$out/not_found.html")" "custom 404 has custom content"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
129
tests/test_config.sh
Executable file
129
tests/test_config.sh
Executable file
@@ -0,0 +1,129 @@
|
||||
test_config_defaults() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
|
||||
assert_eq "kewt" "$title" "default title"
|
||||
assert_eq "kewt" "$style" "default style"
|
||||
assert_eq "en" "$lang" "default lang"
|
||||
assert_eq "false" "$draft_by_default" "default draft_by_default"
|
||||
assert_eq "true" "$dir_indexes" "default dir_indexes"
|
||||
assert_eq "true" "$single_file_index" "default single_file_index"
|
||||
assert_eq "false" "$flatten" "default flatten"
|
||||
assert_eq "Home" "$home_name" "default home_name"
|
||||
assert_eq "true" "$show_home_in_nav" "default show_home_in_nav"
|
||||
assert_eq "false" "$generate_feed" "default generate_feed"
|
||||
assert_eq "rss.xml" "$feed_file" "default feed_file"
|
||||
assert_eq "12" "$posts_per_page" "default posts_per_page"
|
||||
assert_eq "false" "$generate_tags" "default generate_tags"
|
||||
assert_eq "tags" "$tags_dir" "default tags_dir"
|
||||
assert_eq "false" "$generate_search" "default generate_search"
|
||||
assert_eq "true" "$enable_header_links" "default enable_header_links"
|
||||
assert_eq "true" "$cw_hide_url" "default cw_hide_url"
|
||||
}
|
||||
|
||||
test_config_reset() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
title="custom"
|
||||
style="nord"
|
||||
generate_feed="true"
|
||||
reset_config
|
||||
|
||||
assert_eq "kewt" "$title" "reset title"
|
||||
assert_eq "kewt" "$style" "reset style"
|
||||
assert_eq "false" "$generate_feed" "reset generate_feed"
|
||||
}
|
||||
|
||||
test_config_load() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
cat > "$tmpdir/test.conf" <<EOF
|
||||
title = "My Site"
|
||||
style = "nord"
|
||||
generate_feed = true
|
||||
base_url = "https://example.com"
|
||||
posts_per_page = 5
|
||||
EOF
|
||||
|
||||
reset_config
|
||||
load_config "$tmpdir/test.conf"
|
||||
|
||||
assert_eq "My Site" "$title" "load title"
|
||||
assert_eq "nord" "$style" "load style"
|
||||
assert_eq "true" "$generate_feed" "load generate_feed"
|
||||
assert_eq "https://example.com" "$base_url" "load base_url"
|
||||
assert_eq "5" "$posts_per_page" "load posts_per_page"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_config_load_quoted() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
cat > "$tmpdir/test.conf" <<'EOF'
|
||||
footer = "made with <a href=\"https://kewt.krzak.org\">kewt</a>"
|
||||
nav_links = "[Docs](/docs), [About](/about)"
|
||||
EOF
|
||||
|
||||
reset_config
|
||||
load_config "$tmpdir/test.conf"
|
||||
|
||||
assert_eq 'made with <a href="https://kewt.krzak.org">kewt</a>' "$footer" "load quoted footer"
|
||||
assert_eq "[Docs](/docs), [About](/about)" "$nav_links" "load quoted nav_links"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_config_load_skips_comments() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
cat > "$tmpdir/test.conf" <<EOF
|
||||
# This is a comment
|
||||
title = "Test Site"
|
||||
# Another comment
|
||||
style = "kewt"
|
||||
EOF
|
||||
|
||||
reset_config
|
||||
load_config "$tmpdir/test.conf"
|
||||
|
||||
assert_eq "Test Site" "$title" "load with comments title"
|
||||
assert_eq "kewt" "$style" "load with comments style"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_config_load_missing_file() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
reset_config
|
||||
load_config "/nonexistent/path/site.conf"
|
||||
|
||||
assert_eq "kewt" "$title" "missing file keeps defaults"
|
||||
}
|
||||
|
||||
test_config_feed_full_content_default() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
|
||||
assert_eq "false" "$feed_full_content" "default feed_full_content"
|
||||
}
|
||||
|
||||
test_config_feed_full_content_load() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
cat > "$tmpdir/test.conf" <<EOF
|
||||
feed_full_content = true
|
||||
EOF
|
||||
|
||||
reset_config
|
||||
load_config "$tmpdir/test.conf"
|
||||
|
||||
assert_eq "true" "$feed_full_content" "load feed_full_content"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
43
tests/test_manifest.sh
Normal file
43
tests/test_manifest.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
test_draft_mode_includes_drafts_in_manifest() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
. "$project_dir/lib/manifest.sh"
|
||||
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
|
||||
cat > "$tmpdir/site.conf" <<EOF
|
||||
title = "Test"
|
||||
dir_indexes = true
|
||||
EOF
|
||||
|
||||
printf '# Normal Page\n' > "$tmpdir/normal.md"
|
||||
printf -- '---\ndraft = true\n---\n# Draft Page\n' > "$tmpdir/draft.md"
|
||||
|
||||
src="$tmpdir"
|
||||
out="$tmpdir/out"
|
||||
KEWT_TMPDIR="$tmpdir/tmp"
|
||||
mkdir -p "$KEWT_TMPDIR"
|
||||
|
||||
awk_dir="$project_dir/awk"
|
||||
script_dir="$project_dir"
|
||||
|
||||
reset_config
|
||||
load_config "$tmpdir/site.conf"
|
||||
IGNORE_ARGS="-name '.kewtignore' -o -path '$src/.*'"
|
||||
HIDE_ARGS="-name '.kewtignore' -o -name '.kewthide' -o -name '.kewtpreserve' -o -path '$src/.*'"
|
||||
PRESERVE_ARGS="-false"
|
||||
|
||||
draft_mode="false"
|
||||
build_markdown_manifest
|
||||
visible_count=$(wc -l < "$manifest_visible_list")
|
||||
assert_eq "1" "$visible_count" "without draft mode only normal page visible"
|
||||
|
||||
draft_mode="true"
|
||||
build_markdown_manifest
|
||||
visible_count=$(wc -l < "$manifest_visible_list")
|
||||
assert_eq "2" "$visible_count" "with draft mode both pages visible"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
146
tests/test_markdown.sh
Normal file
146
tests/test_markdown.sh
Normal file
@@ -0,0 +1,146 @@
|
||||
test_markdown_heading() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf '# Hello World\n\nSome content.\n' > "$tmpdir/test.md"
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<h1" "$result" "markdown heading tag"
|
||||
assert_contains "Hello World" "$result" "markdown heading text"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_paragraph() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf 'This is a paragraph.\n' > "$tmpdir/test.md"
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<p>" "$result" "markdown paragraph open"
|
||||
assert_contains "This is a paragraph." "$result" "markdown paragraph text"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_bold() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf 'This is **bold** text.\n' > "$tmpdir/test.md"
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<strong>bold</strong>" "$result" "markdown bold"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_italic() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf 'This is *italic* text.\n' > "$tmpdir/test.md"
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<em>italic</em>" "$result" "markdown italic"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_link() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf '[click here](https://example.com)\n' > "$tmpdir/test.md"
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains 'href="https://example.com"' "$result" "markdown link href"
|
||||
assert_contains "click here" "$result" "markdown link text"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_code_block() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
cat > "$tmpdir/test.md" <<EOF
|
||||
\`\`\`
|
||||
code here
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<pre>" "$result" "markdown code block pre"
|
||||
assert_contains "<code>" "$result" "markdown code block code"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_unordered_list() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf -- '- item one\n- item two\n' > "$tmpdir/test.md"
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<ul>" "$result" "markdown list ul"
|
||||
assert_contains "<li>item one</li>" "$result" "markdown list item one"
|
||||
assert_contains "<li>item two</li>" "$result" "markdown list item two"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_blockquote() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf '> This is a quote\n' > "$tmpdir/test.md"
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<blockquote>" "$result" "markdown blockquote"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_frontmatter_stripped() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
cat > "$tmpdir/test.md" <<EOF
|
||||
---
|
||||
title = "Test"
|
||||
---
|
||||
|
||||
# Heading
|
||||
EOF
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<h1" "$result" "markdown frontmatter heading present"
|
||||
assert_contains "Heading" "$result" "markdown frontmatter heading text"
|
||||
result_not_contains=$(echo "$result" | grep -c 'title = "Test"')
|
||||
assert_eq "0" "$result_not_contains" "markdown frontmatter not in output"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_header_links() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf '# My Heading\n' > "$tmpdir/test.md"
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="true" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains 'id="my-heading"' "$result" "markdown header link id"
|
||||
assert_contains 'href="#my-heading"' "$result" "markdown header link href"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_markdown_pipe_table() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
cat > "$tmpdir/test.md" <<EOF
|
||||
| A | B |
|
||||
|---|---|
|
||||
| 1 | 2 |
|
||||
EOF
|
||||
|
||||
result=$(ENABLE_HEADER_LINKS="false" CUSTOM_ADMONITIONS="" MARKDOWN_SITE_ROOT="$tmpdir" MARKDOWN_FALLBACK_FILE="" sh "$project_dir/markdown.sh" "$tmpdir/test.md")
|
||||
assert_contains "<table>" "$result" "markdown table"
|
||||
assert_contains "<th>A</th>" "$result" "markdown table header"
|
||||
assert_contains "<td>1</td>" "$result" "markdown table cell"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
92
tests/test_metadata.sh
Executable file
92
tests/test_metadata.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
test_strip_markdown_bold() {
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
result=$(strip_markdown_text "**bold text**")
|
||||
assert_eq "bold text" "$result" "strip bold"
|
||||
}
|
||||
|
||||
test_strip_markdown_italic() {
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
result=$(strip_markdown_text "*italic text*")
|
||||
assert_eq "italic text" "$result" "strip italic"
|
||||
}
|
||||
|
||||
test_strip_markdown_link() {
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
result=$(strip_markdown_text "[link](http://example.com)")
|
||||
assert_eq "link" "$result" "strip link"
|
||||
}
|
||||
|
||||
test_strip_markdown_code() {
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
result=$(strip_markdown_text "\`code\`")
|
||||
assert_eq "code" "$result" "strip code"
|
||||
}
|
||||
|
||||
test_first_heading() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf '# Hello World\n\nSome content\n' > "$tmpdir/test.md"
|
||||
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
result=$(first_heading_from_markdown "$tmpdir/test.md")
|
||||
assert_eq "Hello World" "$result" "first heading"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_first_heading_no_heading() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
printf 'Random content\n' > "$tmpdir/test.md"
|
||||
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
result=$(first_heading_from_markdown "$tmpdir/test.md")
|
||||
assert_eq "" "$result" "no heading returns empty"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_parse_frontmatter() {
|
||||
tmpdir="${TMPDIR:-/tmp}/kewt_test.$$"
|
||||
mkdir -p "$tmpdir"
|
||||
cat > "$tmpdir/test.md" <<'EOF'
|
||||
---
|
||||
title = "My Post"
|
||||
date = "2026-05-20 10:00"
|
||||
draft = false
|
||||
description = "A test post"
|
||||
tags = "test, example"
|
||||
---
|
||||
|
||||
# Content
|
||||
EOF
|
||||
|
||||
export KEWT_TMPDIR="$tmpdir"
|
||||
export awk_dir="$project_dir/awk"
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
. "$project_dir/lib/config.sh"
|
||||
|
||||
parse_frontmatter "$tmpdir/test.md"
|
||||
|
||||
assert_eq "My Post" "$fm_title" "parse title"
|
||||
assert_eq "2026-05-20 10:00" "$fm_date" "parse date"
|
||||
assert_eq "false" "$fm_draft" "parse draft"
|
||||
assert_eq "A test post" "$fm_description" "parse description"
|
||||
assert_eq "test, example" "$fm_tags" "parse tags"
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
|
||||
test_set_post_datetime_from_date() {
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
set_post_datetime "2026-05-20 14:30" "fallback"
|
||||
assert_eq "2026-05-20" "$post_date" "post date from date field"
|
||||
assert_eq "14:30" "$post_time" "post time from date field"
|
||||
}
|
||||
|
||||
test_set_post_datetime_from_filename() {
|
||||
. "$project_dir/lib/metadata.sh"
|
||||
set_post_datetime "" "2026-05-20-14-30-slug"
|
||||
assert_eq "2026-05-20" "$post_date" "post date from filename"
|
||||
assert_eq "14:30" "$post_time" "post time from filename"
|
||||
}
|
||||
93
tests/test_runner.sh
Executable file
93
tests/test_runner.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/bin/sh
|
||||
|
||||
script_dir=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
|
||||
project_dir=$(CDPATH="" cd -- "$script_dir/.." && pwd)
|
||||
|
||||
passed=0
|
||||
failed=0
|
||||
total=0
|
||||
|
||||
assert_eq() {
|
||||
total=$((total + 1))
|
||||
expected="$1"
|
||||
actual="$2"
|
||||
label="$3"
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
passed=$((passed + 1))
|
||||
printf " PASS: %s\n" "$label"
|
||||
else
|
||||
failed=$((failed + 1))
|
||||
printf " FAIL: %s\n" "$label"
|
||||
printf " expected: %s\n" "$expected"
|
||||
printf " actual: %s\n" "$actual"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
total=$((total + 1))
|
||||
needle="$1"
|
||||
haystack="$2"
|
||||
label="$3"
|
||||
|
||||
case "$haystack" in
|
||||
*"$needle"*)
|
||||
passed=$((passed + 1))
|
||||
printf " PASS: %s\n" "$label"
|
||||
;;
|
||||
*)
|
||||
failed=$((failed + 1))
|
||||
printf " FAIL: %s\n" "$label"
|
||||
printf " expected to contain: %s\n" "$needle"
|
||||
printf " actual: %s\n" "$haystack"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
assert_file_exists() {
|
||||
total=$((total + 1))
|
||||
path="$1"
|
||||
label="$2"
|
||||
|
||||
if [ -f "$path" ]; then
|
||||
passed=$((passed + 1))
|
||||
printf " PASS: %s\n" "$label"
|
||||
else
|
||||
failed=$((failed + 1))
|
||||
printf " FAIL: %s\n" "$label"
|
||||
printf " file not found: %s\n" "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
run_test_file() {
|
||||
test_file="$1"
|
||||
test_name=$(basename "$test_file" .sh)
|
||||
printf "\n== %s ==\n" "$test_name"
|
||||
|
||||
saved_total=$total
|
||||
saved_passed=$passed
|
||||
saved_failed=$failed
|
||||
|
||||
. "$test_file"
|
||||
|
||||
for func in $(grep '^test_' "$test_file" | sed 's/().*//' | sort -u); do
|
||||
$func
|
||||
done
|
||||
|
||||
file_total=$((total - saved_total))
|
||||
file_passed=$((passed - saved_passed))
|
||||
file_failed=$((failed - saved_failed))
|
||||
if [ "$file_total" -eq 0 ]; then
|
||||
printf " (no tests found)\n"
|
||||
fi
|
||||
}
|
||||
|
||||
for test_file in "$script_dir"/test_*.sh; do
|
||||
[ "$(basename "$test_file")" = "test_runner.sh" ] && continue
|
||||
run_test_file "$test_file"
|
||||
done
|
||||
|
||||
printf "\n== Results ==\n"
|
||||
printf " %d passed, %d failed, %d total\n" "$passed" "$failed" "$total"
|
||||
|
||||
[ "$failed" -eq 0 ] && exit 0 || exit 1
|
||||
88
tests/test_runtime.sh
Executable file
88
tests/test_runtime.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
test_url_encode_spaces() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(encode_url_path "hello world")
|
||||
assert_eq "hello%20world" "$result" "encode spaces"
|
||||
}
|
||||
|
||||
test_url_encode_hash() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(encode_url_path "file#section")
|
||||
assert_eq "file%23section" "$result" "encode hash"
|
||||
}
|
||||
|
||||
test_url_encode_question() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(encode_url_path "search?q=test")
|
||||
assert_eq "search%3Fq=test" "$result" "encode question mark"
|
||||
}
|
||||
|
||||
test_url_encode_percent() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(encode_url_path "100%")
|
||||
assert_eq "100%25" "$result" "encode percent"
|
||||
}
|
||||
|
||||
test_markdown_file_url() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(markdown_file_url "docs/readme.md")
|
||||
assert_eq "/docs/readme.html" "$result" "markdown file url"
|
||||
}
|
||||
|
||||
test_directory_index_url_root() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(directory_index_url "")
|
||||
assert_eq "/index.html" "$result" "root index url"
|
||||
}
|
||||
|
||||
test_directory_index_url_subdir() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(directory_index_url "docs")
|
||||
assert_eq "/docs/index.html" "$result" "subdir index url"
|
||||
}
|
||||
|
||||
test_directory_index_url_dot() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(directory_index_url ".")
|
||||
assert_eq "/index.html" "$result" "dot index url"
|
||||
}
|
||||
|
||||
test_format_rfc2822_utc() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(format_rfc2822_utc "2026-05-20" "14:30")
|
||||
assert_eq "Wed, 20 May 2026 14:30:00 +0000" "$result" "rfc2822 format"
|
||||
}
|
||||
|
||||
test_format_rfc2822_utc_default_time() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(format_rfc2822_utc "2026-01-01")
|
||||
assert_eq "Thu, 01 Jan 2026 00:00:00 +0000" "$result" "rfc2822 default time"
|
||||
}
|
||||
|
||||
test_trim_whitespace() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(trim_whitespace " hello ")
|
||||
assert_eq "hello" "$result" "trim whitespace"
|
||||
}
|
||||
|
||||
test_trim_whitespace_tabs() {
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
result=$(trim_whitespace " world ")
|
||||
assert_eq "world" "$result" "trim tabs"
|
||||
}
|
||||
|
||||
test_is_posts_directory_rel() {
|
||||
. "$project_dir/lib/config.sh"
|
||||
. "$project_dir/lib/runtime.sh"
|
||||
|
||||
posts_dir="posts"
|
||||
|
||||
result=$(is_posts_directory_rel "posts" && echo "true" || echo "false")
|
||||
assert_eq "true" "$result" "posts dir match"
|
||||
|
||||
result=$(is_posts_directory_rel "blog" && echo "true" || echo "false")
|
||||
assert_eq "false" "$result" "posts dir no match"
|
||||
|
||||
posts_dir="./posts"
|
||||
result=$(is_posts_directory_rel "posts" && echo "true" || echo "false")
|
||||
assert_eq "true" "$result" "posts dir with dot prefix config"
|
||||
}
|
||||
@@ -17,10 +17,10 @@ tmpdir=$(mktemp -d "/tmp/kewt.XXXXXX")
|
||||
trap 'rm -rf "$tmpdir"' EXIT HUP INT TERM
|
||||
|
||||
# Extract payload
|
||||
sed '1,/^#==PAYLOAD==$/d' "$0" | tar -xz -C "$tmpdir"
|
||||
LC_ALL=C sed '1,/^#==PAYLOAD==$/d' "$0" | tar -xz -C "$tmpdir"
|
||||
|
||||
# Pass control to the extracted script
|
||||
KEWT_INVOKED_AS="$0" "$tmpdir/kewt.sh" "$@"
|
||||
KEWT_INVOKED_AS="$0" sh "$tmpdir/kewt.sh" "$@"
|
||||
exit $?
|
||||
|
||||
#==PAYLOAD==
|
||||
|
||||
Reference in New Issue
Block a user