-
-
Save rascul/108d357078b4f794cee00400143d886e to your computer and use it in GitHub Desktop.
bash static site generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Feel free to edit this config. Don't remove any options. | |
declare -A config=( | |
# Directories. No trailing slashes. | |
["content"]="content" | |
["output"]="www" | |
["templates"]="templates" | |
# Add .html extention to generated files | |
["extensions"]="no" | |
# Markdown command, must read from stdin and write to stdout | |
["markdown"]="markdown" | |
# This is a command to deploy the site, or possibly launch another script | |
# to deploy it if deployment is complicated. | |
["deploy"]="rsync" | |
# If another server is desired, change this command to start it. | |
["server"]="python -m SimpleHTTPServer 8000 &" | |
# use nginx and the provided config to serve extensionless files as html | |
#["server"]="nginx -c /home/rascul/site/nginx.conf" | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
shopt -s nullglob | |
server_pid= | |
# Load the config | |
if [[ ! -e rc ]]; then | |
echo "rc file not found." | |
exit 1 | |
fi | |
. rc | |
stop_server() { | |
if [[ "$server_pid" ]]; then | |
kill $server_pid | |
fi | |
pkill -P $$ | |
exit | |
} | |
# checks if a source file is newer than the destination file, to determine if | |
# the source files should be generated or copied to the destination, or not | |
# $1 :: source file | |
# $2 :: destination file | |
file_newer() { | |
if [[ -e "$1" && -e "$2" ]]; then | |
if [[ $(stat -c %Y "$1") > $(stat -c %Y "$2") ]]; then | |
return 0 | |
else | |
return 1 | |
fi | |
else | |
return 0 | |
fi | |
} | |
# render a template | |
# $1 :: input file | |
# $2 :: output file | |
render_template() { | |
declare -A matter | |
content= | |
m= | |
# get the front matter, then the content | |
while IFS= read -r line; do | |
if [[ $line =~ ^---*$ ]]; then | |
m=1 | |
elif [[ "$m" ]]; then | |
content+="$line\n" | |
else | |
matter[${line/: *}]="${line/${line/: *}: }" | |
fi | |
done < "$1" | |
if [[ ! "${matter[template]}" ]]; then | |
printf 'no template in front matter\n' >&2 | |
return 1 | |
fi | |
# convert the markdown | |
content=$(printf '%b' "$content" | ${config[markdown]}) | |
t="${config[output]}" | |
matter[path]="${2#$t}" | |
rm -f "$2" | |
run= | |
# fill in the template | |
while IFS= read -r line; do | |
if [[ $line =~ ^[[:blank:]]*\#\ ]]; then | |
t=${line#${line%%[![:space:]]*}} | |
run+="${t:2}\n" | |
line= | |
else | |
if [[ "$run" ]]; then | |
printf '%b' "$run" | /usr/bin/env bash | |
run= | |
fi | |
while [[ $line =~ \{\{\ ([[:alpha:]]+)\ \}\} ]]; do | |
m=${BASH_REMATCH[1]} | |
if [[ $m = "content" ]]; then | |
line=${line//\{\{ $m \}\}/$content} | |
else | |
line=${line//\{\{ $m \}\}/${matter[$m]}} | |
fi | |
done | |
fi | |
if [[ "$line" ]]; then | |
printf '%b\n' "$line" | |
fi | |
done < "${config[templates]}/${matter[template]}" > "$2" | |
} | |
# recursively build the site | |
# $1 :: content directory | |
# $2 :: output directory | |
build_dir() { | |
if [[ ! -d "$2" ]]; then | |
mkdir -p "$2" | |
fi | |
for f in "$1"/*; do | |
o="${f/${config[content]}/${config[output]}}" | |
if [[ -d "$f" ]]; then | |
build_dir "$f" "$o" | |
else | |
if [[ ${f##*.} = md ]]; then | |
if [[ "${config[extensions]}" = "yes" ]]; then | |
o="${o/%.md/.html}" | |
else | |
o="${o/%.md}" | |
fi | |
if file_newer "$f" "$o"; then | |
printf '> %s\n' "$o" | |
render_template "$f" "$o" | |
fi | |
elif file_newer "$f" "$o"; then | |
printf '+ %s\n' "$o" | |
cp "$f" "$o" | |
fi | |
fi | |
done | |
} | |
# build command | |
command_build() { | |
if [[ $1 = "full" ]]; then | |
rm -rf "${config[output]}" | |
fi | |
build_dir "${config[content]}" "${config[output]}" | |
} | |
# serve command | |
command_serve() { | |
echo "* full build and serve" | |
command_build full | |
echo "* press ctrl+c to quit" | |
cd "${config[output]}" | |
${config[server]} & | |
server_pid=$! | |
cd .. | |
while true; do | |
trap stop_server int | |
inotifywait -qqr -e modify,move,create,delete \ | |
"${config[content]}" "${config[templates]}" && | |
command_build | |
done | |
} | |
# what to do? | |
case $1 in | |
help) | |
shift | |
command_help | |
exit | |
;; | |
build) | |
shift | |
command_build "$@" | |
exit | |
;; | |
serve) | |
shift | |
command_serve | |
exit | |
;; | |
esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment