#!/bin/sh -e # # https://rgz.ee/bin/ssg6 # Copyright 2018-2019 Roman Zolotarev # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # get all .md files in the src directory and look for the "#tags", then make a # new file called "tags.md" as well as create an ".md" file for each tag named # after the tag. The tags.md file will have a section for each tag with a link # to its corresponding .md file, and the .md files named after the tags will # have a section for each tag with a link to the tags.md file. make_tag_files() { # get all .md files in the src directory files=$(find src -name "*.md") tags="" # look for the "#tags" in the files for file in $files; do found=$(tr "\n" " " < "$file" | grep -oE "[[:space:]]#[^[:space:]&^[:punct:]]+" || :;) tags="$tags $found" done # remove any duplicate tags tags=$(echo "$tags" | tr " " "\n" | sort -u | tr "\n" " ") echo "$tags" # for each tag in tags add a section to the tags.md file with a link t for tag in $tags; do # remove the "#" from the tag tagName=$(echo "$tag" | sed 's/^#//') echo "# $tag" > src/tag-"$tagName".md echo "" >> src/tag-"$tagName".md echo "" >> src/tag-"$tagName".md done } convert_tags_to_links() { # for each file in files, replace the "#tagname"s with [#tagname](tag-tagname.html) for file in $filesMd; do # if file name contains "index|contact|tag" then skip it if [ "$(echo "$file" | grep -oE "index|contact|tag")" != "" ] ; then continue fi # get just the file name fileName=$(basename "$file") # remove the ".md" from the file name fileName=$(echo "$fileName" | sed 's/\.md//') # get all the tags in the file tags=$(tr "\n" " " < "$file" | grep -oE "[[:space:]]#[^[:space:]&^[:punct:]]+" || :;) # for each tag in tags, replace the "#tagname" with [#tagname](tag-tagname.html) for tag in $tags; do # remove the "#" from the tag tagName=$(echo "$tag" | sed 's/^#//') # replace the tag with [#tagname](tag-tagname.html) sed -i "s|$tag|$tag|g" "public/$fileName.html" done done } render_tags_on_index() { tags="" # look for the "#tags" in the files for file in $filesMd; do found=$(tr "\n" " " < "$file" | grep -oE "[[:space:]]#[^[:space:]&^[:punct:]]+" || :;) tags="$tags $found" done # remove any duplicate tags tags=$(echo "$tags" | tr " " "\n" | sort -u | tr "\n" " ") for tag in $tags; do # remove the "#" from the tag tagName=$(echo "$tag" | sed 's/^#//') tagList="$tagList$tag " done sed -i "s::

Browse by tag

\n

$tagList

\n:" public/index.html } fix_emojis() { # for each file in files, replace the "↩" with "\↩\︎" for file in $files; do sed -i "s|\↩|\↩\︎|g" "$file" done } fix_md_links() { # for each file in files, replace ".md">" with ".html">" for file in $files; do sed -i "s|\.md\">|\.html\">|g" "$file" done } # Not done, still need to figure out how to get this to ignore the logo, and how to correctly regex link_images() { # for each file in files, look for html img tags and wrap them in a link to the image for file in $filesPhoto; do # get all the html img tags in the file # one example of an img tag/match is: dotfile-aliases images=$(grep -oE "|\" />|g" "$file" done } process_html() { # get all .html files in the public directory that are not tag-*.html files=$(find public -name "*.html" | grep -v "tag-") # get all .md files in the src directory filesMd=$(find src -name "*.md") # only files with "photo" in the file name and ignore the tag-*.html files filesPhoto=$(find public -name "*photo*" | grep -v "tag-") convert_tags_to_links render_tags_on_index fix_md_links fix_emojis link_images } main() { test -n "$1" || usage test -n "$2" || usage test -n "$3" || usage test -n "$4" || usage test -d "$1" || no_dir "$1" test -d "$2" || no_dir "$2" src=$(readlink_f "$1") dst=$(readlink_f "$2") IGNORE=$( if ! test -f "$src/.ssgignore"; then printf ' ! -path "*/.*"' return fi while read -r x; do test -n "$x" || continue printf ' ! -path "*/%s*"' "$x" done <"$src/.ssgignore" ) # files title="$3" h_file="$src/_header.html" f_file="$src/_footer.html" test -f "$f_file" && FOOTER=$(cat "$f_file") && export FOOTER test -f "$h_file" && HEADER=$(cat "$h_file") && export HEADER list_dirs "$src" | (cd "$src" && cpio -pdu "$dst") fs=$( if test -f "$dst/.files"; then list_affected_files "$src" "$dst/.files" else list_files "$1" fi ) if test -n "$fs"; then echo "$fs" | tee "$dst/.files" if echo "$fs" | grep -q '\.md$'; then if test -x "$(command -v lowdown 2>/dev/null)"; then echo "$fs" | grep '\.md$' | render_md_files_lowdown "$src" "$dst" "$title" else if test -x "$(command -v Markdown.pl 2>/dev/null)"; then echo "$fs" | grep '\.md$' | render_md_files_Markdown_pl "$src" "$dst" "$title" else echo "couldn't find lowdown nor Markdown.pl" exit 3 fi fi fi echo "$fs" | grep '\.html$' | render_html_files "$src" "$dst" "$title" echo "$fs" | grep -Ev '\.md$|\.html$' | (cd "$src" && cpio -pu "$dst") fi printf '[ssg] ' >&2 print_status 'file, ' 'files, ' "$fs" >&2 # sitemap base_url="$4" date=$(date +%Y-%m-%d) urls=$(list_pages "$src") test -n "$urls" && render_sitemap "$urls" "$base_url" "$date" >"$dst/sitemap.xml" render_article_list "$urls" "$base_url" "$dst" "$src" print_status 'url' 'urls' "$urls" >&2 echo >&2 } readlink_f() { file="$1" cd "$(dirname "$file")" file=$(basename "$file") while test -L "$file"; do file=$(readlink "$file") cd "$(dirname "$file")" file=$(basename "$file") done dir=$(pwd -P) echo "$dir/$file" } print_status() { test -z "$3" && printf 'no %s' "$2" && return echo "$3" | awk -v singular="$1" -v plural="$2" ' END { if (NR==1) printf NR " " singular if (NR>1) printf NR " " plural }' } usage() { echo "usage: ${0##*/} src dst title base_url" >&2 exit 1 } no_dir() { echo "${0##*/}: $1: No such directory" >&2 exit 2 } list_dirs() { cd "$1" && eval "find . -type d ! -name '.' ! -path '*/_*' $IGNORE" } list_files() { cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE" } list_dependant_files() { e="\\( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \\)" cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE $e" } list_newer_files() { cd "$1" && eval "find . -type f ! -name '.' $IGNORE -newer $2" } has_partials() { grep -qE '^./_.*\.html$|^./_.*\.js$|^./_.*\.css$' } list_affected_files() { fs=$(list_newer_files "$1" "$2") if echo "$fs" | has_partials; then list_dependant_files "$1" else echo "$fs" fi } render_html_files() { while read -r f; do render_html_file "$3" <"$1/$f" >"$2/$f" done } render_md_files_lowdown() { while read -r f; do lowdown \ --html-no-escapehtml \ --html-no-skiphtml \ --parse-no-autolink \ --parse-no-metadata < "$1/$f" | render_html_file "$3" > "$2/${f%\.md}.html" done } render_md_files_Markdown_pl() { while read -r f; do Markdown.pl <"$1/$f" | render_html_file "$3" \ >"$2/${f%\.md}.html" done } render_html_file() { # h/t Devin Teske awk -v title="$1" ' { body = body "\n" $0 } END { body = substr(body, 2) if (body ~ /<\/?[Hh][Tt][Mm][Ll]/) { print body exit } if (match(body, /<[[:space:]]*[Hh]1(>|[[:space:]][^>]*>)/)) { t = substr(body, RSTART + RLENGTH) sub("<[[:space:]]*/[[:space:]]*[Hh]1.*", "", t) gsub(/^[[:space:]]*|[[:space:]]$/, "", t) if (t) title = t " — " title } n = split(ENVIRON["HEADER"], header, /\n/) for (i = 1; i <= n; i++) { if (match(tolower(header[i]), "")) { head = substr(header[i], 1, RSTART - 1) tail = substr(header[i], RSTART + RLENGTH) print head "" title "" tail } else print header[i] } print body print ENVIRON["FOOTER"] }' } list_pages() { e="\\( -name '*.html' -o -name '*.md' \\)" cd "$1" && eval "find . -type f ! -path '*/.*' ! -path '*/_*' $IGNORE $e" | sed 's#^./##;s#.md$#.html#;s#/index.html$#/#' } render_article_list() { urls="$1" echo "$urls" base_url="$2" items="" for i in $1; do if ! echo "$i" | grep -Eq "index|contact|tag"; then page_date=$(awk 'NR==3' "$4/${i%\.html}.md") page_date_clean=$(echo "$page_date" | sed 's/<[^>]*>//g') # get the year from the date year=$(echo "$page_date_clean" | cut -d ',' -f 2) # get the month from page_date_clean, make sure to remove everything after # the space month=$(echo "$page_date_clean" | cut -d ' ' -f 1) # convert month string "january" to number month_num=$(date -d "$month 01, $year" +%m) # get the day from date # if there is a sequence like " 4th," remove everything that is not a # number day=$(echo "$page_date_clean" | cut -d ' ' -f 2 | sed 's/[^0-9]//g') day_num=$(date -d "$month $day, $year" +%d) # combine year, month, and day in string format sortable_title="$year-$month_num-$day_num:$i" echo "$sortable_title" title_list="$title_list""$sortable_title" #perhaps could be an issue fi done # sort the list of titles title_list=$(echo "$title_list" | tr " " "\n" | sort -r | tr "\n" " ") echo "$title_list" for i in $title_list; do if ! echo "$i" | grep -Eq "index|contact|tag"; then url=$(echo "$i" | cut -d ':' -f 2) echo "$url" page_title=$(awk 'NR==1 {print substr($0, 3)}' "$4/${url%\.html}.md") page_date=$(awk 'NR==3' "$4/${url%\.html}.md") echo "$page_title" item="
  • ${page_title} \↩\︎${page_date}
  • " items="$items""$item" fi done sed -i "s|||g" "$3/index.html" } render_sitemap() { urls="$1" base_url="$2" date="$3" echo '' echo '' echo "$urls" | sed -E 's#^(.*)$#'"$base_url"'/\1'"$date"'1.0#' echo '' } make_tag_files main "$@" process_html