aboutsummaryrefslogblamecommitdiff
path: root/v2/src/development/genhtml.sh
blob: 3f1bca6b5c914536f8990dd72fba9c8485061441 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                                                         













































































                                                                                     


































































                                                                               
                                                                                                                                              


















































                                                                                                                                                                                             
                                                                























































                                                                                                         

                                                                                                 







                                                                 

                                                                             




                    

                                                                           




















                                                                                                                 
                                                                                                                                            


                                                                    

                                                                                                                                    








                                                                                                                                                                                                                                                                                                                                                     
#!/bin/sh
set -eu

# FIXMEs:
# - feeds
# - link to next and/or previous in <head>
# - translation support
# - validate input variables: regex for date (same as _plugins/linter.rb)
# - `date -d` isn't POSIX
# - parse commonmark and use a custom HTML emitter over <pre><code> regex
# - handle mixture of personal scripts
# - sitemap?  How does it even work?
# - dark mode
# - generate security.txt dynamically
# - config.env should depend on dynamic.mk?



usage() {
	cat <<-'EOF'
		Usage:
		  genhtml.sh FILENAME
		  genhtml.sh -h
	EOF
}

help() {
	cat <<-'EOF'

		Options:
		  -h, --help    show this message

		  FILENAME      the name of the input file, to also be used as
		                URL


		Process the FILENAME, and generate a full HTML page.

		The FILENAME is used to infer the output URL, by removing the
		`src/content/` prefix, and replacing the trailing `.md` with
		`.html`.  This URL is used to build the self-referencing
		"canonical" link, extracting plaintext snippets, etc.


		Examples:

		  Generate the HTML for a pastebin:

		    $ sh genhtml.sh src/content/a-paste.md > src/content/a-paste.html
	EOF
}


for f in "$@"; do
	case "$f" in
		--)
			break
			;;
		--help)
			usage
			help
			exit
			;;
		*)
			;;
	esac
done

while getopts 'h' flag; do
	case "$flag" in
		h)
			usage
			help
			exit
			;;
		*)
			usage >&2
			exit 2
			;;
	esac
done
shift $((OPTIND - 1))


FILENAME="${1:-}"
eval "$(assert-arg "$FILENAME" 'FILENAME')"


#
# Utility functions
#

translate() {
	printf '%s' "$1"
}

_() {
	translate "$1" | html
}

SEEN_SLUGS="$(mkstemp)"
slugify_once() {
	SLUG="$(printf '%s' "$1" | slugify)${2:+-$2}"
	if grep -q "^$SLUG$" "$SEEN_SLUGS"; then
		N="${2:-0}"
		N=$((N + 1))
		slugify_once "$1" "$N"
	else
		printf '%s\n' "$SLUG" >> "$SEEN_SLUGS"
		printf '%s' "$SLUG"
	fi
}

extract_content() {
	awk '
		separator >= 2
		/^---$/ { separator++ }
	'
}

add_preamble() {
	printf '%s\n%s\n' "$PREAMBLE" "$(cat -)"
}

markdown_to_html() {
	md2html
}

extract_plaintext_snippets() {
	F="$(mkstemp)"
	cat > "$F"
	(
		IFS=''
		BLOCK_NUMBER=0
		IN_BLOCK=
		while read -r line; do
			if [ "$line" = '</code></pre>' ]; then
				IN_BLOCK=
			fi

			if [ -n "$IN_BLOCK" ]; then
				printf '%s\n' "$line" | html -d >> "$OUT"
			fi

			if printf '%s' "$line" | grep -q '^<pre><code.*>'; then
				IN_BLOCK=1
				OUT="${FILENAME%.md}.html.$BLOCK_NUMBER.txt"
				BLOCK_NUMBER=$((BLOCK_NUMBER + 1))
				printf '%s\n' "$line" |
					sed 's|^\(<pre><code.*>\)\(.*\)$|\2|' |
					html -d > "$OUT"
			fi
		done < "$F"

		BLOCK_NUMBER=0
		while read -r line; do
			printf '%s\n' "$line"

			if [ "$line" = '</code></pre>' ]; then
				printf '<p class="plaintext-link"><a href="%s.%s.txt">plaintext</a></p>\n' "$(url-for "$URL")" "$BLOCK_NUMBER"
				BLOCK_NUMBER=$((BLOCK_NUMBER + 1))
			fi
		done < "$F"
	)

}

add_line_numbers() {
	awk '
		/^<\/code><\/pre>$/ {
			in_block = 0
			printf "</tbody></table>%s\n", $0
			next
		}

		match($0, /^(<pre><code.*>)(.*)$/, a) {
			printf "%s<table rules=columns class=\"code-block\"><tbody>", a[1]

			n = 1
			block_count++
			printf "<tr><td class=\"line-number\"><a id=\"B%s-L%s\" href=\"#B%s-L%s\">%s</a></td><td class=\"code-line\">%s</td></tr>\n", block_count, n, block_count, n, n, a[2]
			in_block = 1
			next
		}

		in_block == 1 {
			n++
			printf "<tr><td class=\"line-number\"><a id=\"B%s-L%s\" href=\"#B%s-L%s\">%s</a></td><td class=\"code-line\">%s</td></tr>\n", block_count, n, block_count, n, n, $0
			next
		}

		{ print }
	'
}

add_headings_anchors() {
	(
		IFS=''
		while read -r line; do
			if ! printf '%s' "$line" | grep -q '^<h[2-6]>'; then
				printf '%s\n' "$line"
				continue
			fi
			LVL="$(printf '%s' "$line" | sed 's|^<h\(.\)>.*|\1|')"
			HEADING="$(printf '%s' "$line" | sed 's|^<h.>\(.*\)</h.>$|\1|')"
			SLUG="$(slugify_once "$HEADING")"
			printf '<h%s class="header-anchor" id="%s">%s<a href="#%s" aria-hidden="true"><img class="svg-icon" src="%s" /></a></h%s>\n' \
				"$LVL"     \
				"$SLUG"    \
				"$HEADING" \
				"$SLUG"    \
				"$(url-for 'static/link.svg')" \
				"$LVL"
		done
	)
}

emit_body() {
	cat "$FILENAME" |
		extract_content            |
		add_preamble               |
		markdown_to_html           |
		extract_plaintext_snippets |
		add_line_numbers           |
		add_headings_anchors
}


#
# Environment variables
#

. src/development/config.env

eval "$(
	awk '
		/^---$/ { if (++separator > 1) exit; else next; }
		{ print }
	' "$FILENAME"
)"

SITE_NAME="$(_ "EuAndreh's website")"
TITLE="$(_ "${TITLE:-$SITE_NAME}")"
URI_TITLE="$(printf '%s' "$TITLE" | uri)"
URL="$(printf '%s' "$FILENAME" | sed -e 's|^src/content/||' -e 's|md$|html|')"

PREAMBLE="$(
	cat <<-EOF
		# $TITLE

		<p class="timestamp">
		  Posted on <time datetime="$DATE">$(LANG="$LANGUAGE" date -d "$DATE" "$DATE_FMT")</time>
		</p>
	EOF
)"



#
# Main: generate the HTML to STDOUT.
#

cat <<-EOF
	<!DOCTYPE html>
	<html lang="$LANGUAGE">
	  <head>
	    <meta charset="UTF-8" />
	    <meta name="viewport" content="width=device-width, initial-scale=1" />
	    <link rel="stylesheet" type="text/css"      href="$(url-for 'static/styles.css')" />
	    <link rel="icon"       type="image/svg+xml" href="$(url-for 'static/favicon.svg')" />

	    <title>$TITLE</title>

	    <meta name="author" content="EuAndreh" />
	    <meta property="og:site_name" content="$SITE_NAME" />
	    <meta property="og:locale"    content="$LANGUAGE" />
	    <meta property="og:title"     content="$TITLE" />

	    <link rel="canonical"      href="$(url-for "$URL" | absolute)" />
	    <meta property="og:url" content="$(url-for "$URL" | absolute)" />
	  </head>
	  <body>
	    <header>
	      <nav>
	        <ul>
	          <a href="$(url-for "$LANGUAGE/")">$(_ 'EuAndreh')</a>
	          <a href="$(url-for "$(_ 'about.html')")">$(_ 'About')</a>
	        </ul>
	      </nav>
	      <hr />
	    </header>
	    <main>
	      <article>
	        $(emit_body)
	        <hr />
	        <p class="post-footer">
	          <a href="mailto:~euandreh/public-inbox@lists.sr.ht?Subject=Re%3A%20$URI_TITLE">Comment</a>
	          and see
	          <a href="https://lists.sr.ht/~euandreh/public-inbox?search=$URI_TITLE">existing discussions</a>
	          |
	          <a href="https://euandreh.xyz/euandre.org.git/tree/$FILENAME">view source</a>
	        </p>
	      </article>
	    </main>
	    <footer>
	      <hr />
	      <ul>
	        <li>
	          <img class="svg-icon" src="$(url-for 'static/envelope.svg')" alt="$(_ 'a envelope icon representing an email address')" />
	          <a href="mailto:eu@euandre.org">eu@euandre.org</a>
	        </li>
	        <li>
	          <img class="svg-icon" src="$(url-for 'static/lock.svg')" alt="$(_ 'a lock icon representing a GPG public key')" />
	          <a href="$(url-for 'static/public.asc.txt')">81F90EC3CD356060</a>
	        </li>
	      </ul>
	      <p>
	        $(translate 'The content for this site is licensed under <a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>.  The <a href="https://euandreh.xyz/euandre.org.git">code</a> is <a rel="license" href="https://euandreh.xyz/euandre.org.git/tree/COPYING">AGPLv3 or later</a>.  Patches welcome.')
	      </p>
	    </footer>
	  </body>
	</html>
EOF