aboutsummaryrefslogtreecommitdiffstats
path: root/build.sh
blob: 2973c2ff3a2175158021d5970ebfa400e750bfa1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#!/bin/bash
# build.sh - ubergeek static site generator
# Converts the wiki markdown structure into HTML using lowdown + mo.

[[ "$(basename "${0}")" == "build.sh" ]] || { echo "Do not source build.sh"; exit 1; }

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ ! -f "${SCRIPT_DIR}/build/mo" ]]; then
    echo "[error] build/mo not found. Run: curl -sL https://raw.githubusercontent.com/tests-always-included/mo/master/mo -o build/mo && chmod +x build/mo" >&2
    exit 1
fi
. "${SCRIPT_DIR}/build/mo"

FORCE=false
while [[ $# -gt 0 ]]; do
    case "$1" in
        -f|--force) FORCE=true ;;
        *) echo "[error] Unknown option: $1" >&2; exit 1 ;;
    esac
    shift
done

TMPDIR_BUILD="$(mktemp -d)"
cleanup() { rm -rf "${TMPDIR_BUILD}"; }
trap cleanup EXIT INT TERM

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

get_frontmatter() {
    local key="$1" file="$2"
    lowdown -X "${key}" "${file}" 2>/dev/null || true
}

check_has_code() {
    local html="$1"
    if echo "${html}" | grep -qE '<pre><code|<code class='; then
        HAS_CODE="true"
    else
        HAS_CODE=""
    fi
}

render_page() {
    local template="$1" outfile="$2"
    mkdir -p "$(dirname "${outfile}")"
    local tname="${template##build/templates/}"
    cp "${template}" "build/templates/partials/${tname}"
    (cd build/templates/partials && mo "${tname}") > "${outfile}"
    rm -f "build/templates/partials/${tname}"
}

build_breadcrumbs() {
    local path="$1"
    BREADCRUMBS=""
    local components="${path}"
    while [[ "${components}" != "." ]]; do
        BREADCRUMBS=" / <a href=\"/${components}\">$(basename "${components}")</a>${BREADCRUMBS}"
        components="$(dirname "${components}")"
    done
}

# ---------------------------------------------------------------------------
# process_dir dir
# Recursively renders topic and module pages, returns a markdown list
# of direct children into ${TMPDIR_BUILD}/${dir//\//_}.list.md
# ---------------------------------------------------------------------------
process_dir() {
    local dir="$1"
    local list_file="${TMPDIR_BUILD}/${dir//\//_}.list.md"
    : > "${list_file}"

    local find_list="${TMPDIR_BUILD}/${dir//\//_}.find.list"
    find "${dir}" -mindepth 1 -maxdepth 1 -not -name '.*' -print0 | sort -z > "${find_list}"

    while IFS= read -r -d '' node; do
        if [[ -d "${node}" ]]; then
            local subdir_title
            subdir_title="$(get_frontmatter title "${node}/index.md" 2>/dev/null || true)"
            [[ -z "${subdir_title}" ]] && subdir_title="$(basename "${node}")"
            printf -- '- [%s](/%s)\n' "${subdir_title}" "${node}" >> "${list_file}"
            process_dir "${node}"

        elif [[ "${node}" == *.md && "$(basename "${node}")" != "index.md" ]]; then
            local slug="${node%.md}"
            local title date lang
            title="$(get_frontmatter title "${node}")"
            [[ -z "${title}" ]] && title="$(basename "${slug}")"
            date="$(get_frontmatter date "${node}")"
            lang="$(get_frontmatter lang "${node}")"

            local out_html="output/${slug}/index.html"
            local content
            content="$(lowdown "${node}")"
            check_has_code "${content}"
            build_breadcrumbs "${slug}"

            export PAGE_TITLE="${title}"
            export CONTENT="${content}"
            export BREADCRUMBS
            export HEADER_EXTRA="${date}"
            export META_DATE="${date}"
            export META_DESCRIPTION=""
            export HAS_CODE
            export LANG="${lang:-en}"
            export DIR=""

            render_page "build/templates/page.html" "${out_html}"
            printf -- '- [%s](/%s)\n' "${title}" "${slug}" >> "${list_file}"
            echo "[built] ${out_html}"
        fi
    done < "${find_list}"

    # Build this directory's index page
    local index_src="${dir}/index.md"
    local index_md="${TMPDIR_BUILD}/${dir//\//_}.index.md"
    local dir_title

    if [[ -f "${index_src}" ]]; then
        dir_title="$(get_frontmatter title "${index_src}")"
        cp "${index_src}" "${index_md}"
    else
        dir_title="$(basename "${dir}")"
        printf '# %s\n\n' "${dir_title}" > "${index_md}"
    fi
    printf '\n\n' >> "${index_md}"
    cat "${list_file}" >> "${index_md}"

    local out_html="output/${dir}/index.html"
    local content
    content="$(lowdown "${index_md}")"
    check_has_code "${content}"
    build_breadcrumbs "${dir}"

    export PAGE_TITLE="${dir_title}"
    export CONTENT="${content}"
    export BREADCRUMBS
    export HEADER_EXTRA=""
    export META_DATE=""
    export META_DESCRIPTION=""
    export HAS_CODE
    export LANG="en"
    export DIR=""

    render_page "build/templates/page.html" "${out_html}"
    echo "[built] output/${dir}/index.html"
}

# ---------------------------------------------------------------------------
# Main build
# ---------------------------------------------------------------------------

mkdir -p output

# Discover top-level topic directories (exclude hidden, build, output)
topics_list="${TMPDIR_BUILD}/topics.list.md"
: > "${topics_list}"

find_list="${TMPDIR_BUILD}/toplevel.find.list"
find . -mindepth 1 -maxdepth 1 -type d \
    -not -name '.*' -not -name 'build' -not -name 'output' \
    -print0 | sort -z > "${find_list}"

while IFS= read -r -d '' topic; do
    topic="${topic#./}"
    topic_title="$(get_frontmatter title "${topic}/index.md" 2>/dev/null || true)"
    [[ -z "${topic_title}" ]] && topic_title="${topic}"
    printf -- '- [%s](/%s)\n' "${topic_title}" "${topic}" >> "${topics_list}"
    process_dir "${topic}"
done < "${find_list}"

# Home page — README.md + topic listing
home_md="${TMPDIR_BUILD}/home.md"
cp README.md "${home_md}"
printf '\n\n## topics:\n\n' >> "${home_md}"
cat "${topics_list}" >> "${home_md}"

content="$(lowdown "${home_md}")"
check_has_code "${content}"

export PAGE_TITLE=""
export CONTENT="${content}"
export BREADCRUMBS=""
export HEADER_EXTRA=""
export META_DATE=""
export META_DESCRIPTION=""
export HAS_CODE
export LANG="en"
export DIR=""

render_page "build/templates/home.html" "output/index.html"
echo "[built] output/index.html"

# License page
if [[ -f "LICENSE.md" ]]; then
    content="$(lowdown LICENSE.md)"
    check_has_code "${content}"
    export PAGE_TITLE="license"
    export CONTENT="${content}"
    export BREADCRUMBS=" / <a href=\"/license\">license</a>"
    export HEADER_EXTRA=""
    export META_DATE=""
    export META_DESCRIPTION=""
    export HAS_CODE
    export LANG="en"
    export DIR=""
    render_page "build/templates/page.html" "output/license/index.html"
    echo "[built] output/license/index.html"
fi

echo ""
echo "[done] Build complete. Output in output/"