diff options
| author | David T. Sadler <davidtsadler@googlemail.com> | 2021-05-07 17:11:05 +0100 |
|---|---|---|
| committer | David T. Sadler <davidtsadler@googlemail.com> | 2021-05-07 17:11:05 +0100 |
| commit | 6b0d2b51f546a9964f18b58e794ade7bd7096475 (patch) | |
| tree | f27bdb3fd5a04199a9ebc58e5a29588547a0c7c7 | |
| parent | c709d0c51f03b3010d8f2f19ee73f96c6a06ffe0 (diff) | |
Add build script
| -rw-r--r-- | html_templates/default.html | 12 | ||||
| -rw-r--r-- | scripts/build.php | 112 | ||||
| -rw-r--r-- | scripts/functions.php | 312 | ||||
| -rw-r--r-- | www_assets/images/favicon.png | bin | 0 -> 190 bytes |
4 files changed, 436 insertions, 0 deletions
diff --git a/html_templates/default.html b/html_templates/default.html new file mode 100644 index 0000000..21abb9a --- /dev/null +++ b/html_templates/default.html @@ -0,0 +1,12 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>{{ $title }}</title> + <link rel="shortcut icon" href="/images/favicon.png"> + </head> + <body> + <section>{{ $contents }}</section> + </body> +</html> diff --git a/scripts/build.php b/scripts/build.php new file mode 100644 index 0000000..ecb0d9d --- /dev/null +++ b/scripts/build.php @@ -0,0 +1,112 @@ +<?php declare(strict_types=1); + +require_once __DIR__.DIRECTORY_SEPARATOR.'functions.php'; + +$hostname = null; +$output = null; + +$opts = getopt( + 'h:o:', + [ + 'hostname:', + 'help::', + 'output:', + ] +); + +if ($opts === false) { + usage(); +} + +foreach($opts as $opt => $value) { + if ($value === false) { + continue; + } + + switch ($opt) + { + case 'h': + case 'hostname': + $hostname = $value; + break; + case 'help': + usage(); + break; + case 'o': + case 'output': + $output = $value; + break; + } +} + + +if (!$hostname || !$output) { + usage(); +} + +if (!is_dir($output)) { + echo "Unable to locate specified output directory $output.\n\n"; + exit(1); +} + +if (!is_writable($output)) { + echo "Output directory $output is not writable.\n\n"; + exit(1); +} + +$geminiOutput = "$output/gemini"; + +if (file_exists($geminiOutput)) { + echo "Gemini build directory $geminiOutput already exists.\n\n"; + exit(1); +} + +$wwwOutput = "$output/www"; + +if (file_exists($wwwOutput)) { + echo "WWW build directory $wwwOutput already exists.\n\n"; + exit(1); +} + +$src = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'src'; + +if (!is_dir($src)) { + echo "Unable to locate src directory $src.\n\n"; + exit(1); +} + +if (!is_readable($src)) { + echo "Src directory $src is not readable.\n\n"; + exit(1); +} + +$htmlTemplateDiretory = __DIR__.DIRECTORY_SEPARATOR.'../html_templates'; + +if (!is_dir($htmlTemplateDiretory)) { + echo "Unable to locate html template directory $htmlTemplateDiretory.\n\n"; + exit(1); +} + +if (!is_readable($htmlTemplateDiretory)) { + echo "Src directory $htmlTemplateDiretory is not readable.\n\n"; + exit(1); +} + +$assetsDiretory = __DIR__.DIRECTORY_SEPARATOR.'../www_assets'; + +if (!is_dir($assetsDiretory)) { + echo "Unable to locate assets directory $assetsDiretory.\n\n"; + exit(1); +} + +if (!is_readable($assetsDiretory)) { + echo "Src directory $assetsDiretorY is not readable.\n\n"; + exit(1); +} + +$siteMetaData = getSiteMetaData($src); + +buildGeminiSite($siteMetaData, $geminiOutput); + +buildWWWSite($siteMetaData, $wwwOutput, $htmlTemplateDiretory, $assetsDiretory); + diff --git a/scripts/functions.php b/scripts/functions.php new file mode 100644 index 0000000..de563fc --- /dev/null +++ b/scripts/functions.php @@ -0,0 +1,312 @@ +<?php declare(strict_types=1); + +const HEADER = '/^(#+)\s+(.*)/'; +const LINK = '/^=>\s+(\S+)\s*(.*)/'; +const LIST_ITEM = '/^\*\s+(.*)/'; +const PRE = '/^```(.*)?/'; +const QUOTE = '/^>\s+(.*)/'; + +function usage(): void +{ + echo "Usage: php build.php [options] + + -h --hostname <hostname> Hostname of site. Used when generating HTML. + --help Displays this message. + -o --output <directory> Directory where site will be built into. + +"; + exit(1); +} + +function getSiteMetaData(string $src): array +{ + $src = realpath($src); + + $iter = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($src, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::SELF_FIRST + ); + + $metaData = []; + + foreach ($iter as $fileInfo) { + if (!is_file($fileInfo->getRealPath())) { + continue; + } + $metaData[] = getFileMetaData($src, $fileInfo); + } + + return $metaData; +} + +function getFileMetaData(string $src, \SplFileInfo $fileInfo): array +{ + $input = $fileInfo->getRealPath(); + + $url = str_replace($src, '', $input); + + $urlData = parseUrl($url); + $contentData = parseContent(file_get_contents($input)); + + return [ + 'input' => $input, + 'url' => $url, + 'date' => $urlData['date'], + 'category' => $urlData['category'], + 'isPost' => $urlData['isPost'], + 'title' => $contentData['title'], + 'author' => $contentData['author'], + ]; +} + +function parseUrl(string $url): array +{ + $date = null; + $category = null; + $isPost = false; + + /** + * Assume that only posts have both a date and a category. + */ + if (preg_match('/\/posts\/(.+)\/(\d\d\d\d-\d\d-\d\d)\//', $url, $matches) === 1) { + $date = $matches[2]; + $category = $matches[1]; + $isPost = true; + } + + return [ + 'date' => $date, + 'category' => $category, + 'isPost' => $isPost, + ]; +} + +function parseContent(string $content): array +{ + $title = null; + $author = null; + + if (preg_match('/^# (.+)$/m', $content, $matches) === 1) { + $title = $matches[1]; + } + + if (preg_match('/^> .+ By (.+)$/m', $content, $matches) === 1) { + $author = $matches[1]; + } + + return [ + 'title' => $title, + 'author' => $author, + ]; +} + +function buildGeminiSite(array $siteMetaData, string $output): void +{ + foreach ($siteMetaData as $fileMetaData) { + $destFile = $output.DIRECTORY_SEPARATOR.$fileMetaData['url']; + + $destDirectory = dirname($destFile); + + if (!file_exists($destDirectory) && !mkdir($destDirectory, 0777, true)) { + echo "Unable to create Gemini site directory $destDirectory."; + exit(1); + } + + copy($fileMetaData['input'], $destFile); + } +} + +function buildWWWSite(array $siteMetaData, string $output, string $htmlTemplateDiretory, $assetsDiretory): void +{ + foreach ($siteMetaData as $fileMetaData) { + $destFile = $output.DIRECTORY_SEPARATOR.$fileMetaData['url']; + + $destDirectory = dirname($destFile); + + if (!file_exists($destDirectory) && !mkdir($destDirectory, 0777, true)) { + echo "Unable to create WWW site directory $destDirectory."; + exit(1); + } + + file_put_contents( + str_replace('.gmi', '.html', $destFile), + buildHtmlFile( + $fileMetaData, + gemtext2hmtl(file_get_contents($fileMetaData['input'])), + $htmlTemplateDiretory + ) + ); + } + + copyWWWAssets($assetsDiretory, $output); +} + +function buildHtmlFile(array $fileMetaData, string $contents, string $htmlTemplateDiretory): string +{ + return str_replace( + [ + '{{ $title }}', + '{{ $contents }}' + ], + [ + $fileMetaData['title'], + $contents, + ], + file_get_contents($htmlTemplateDiretory.DIRECTORY_SEPARATOR.'default.html') + ); +} + +function gemtext2hmtl(string $gemtext): string +{ + return renderGemtextTokens(parseGemtext($gemtext)); +} + +function parseGemtext(string $gemtext): array +{ + $tokens = []; + + $lines = preg_split('/\r?\n/', htmlspecialchars($gemtext)); + + $index = 0; + $numLines = count($lines); + $line = fn($index) => trim($lines[$index]); + + while($index < $numLines) { + if (preg_match(HEADER, $line($index), $matches) === 1) { + [, $levels, $content] = $matches; + + $tokens[] = [ + 'type' => HEADER, + 'level' => strlen($levels), + 'content' => $content, + ]; + } elseif (preg_match(LINK, $line($index), $matches) === 1) { + [, $href, $content] = $matches; + + $tokens[] = [ + 'type' => LINK, + 'href' => $href, + 'content' => $content != '' ? $content : $href, + ]; + } elseif (preg_match(LIST_ITEM, $line($index), $matches) === 1) { + $items = []; + + while ($index < $numLines) { + if (preg_match(LIST_ITEM, $line($index), $matches) === 0) { + break; + } + + [, $content] = $matches; + + $items[] = $content; + + $index++; + + } + + $index--; + + $tokens[] = [ + 'type' => LIST_ITEM, + 'items' => $items, + ]; + } elseif (preg_match(PRE, $line($index), $matches) === 1) { + [, $alt] = $matches; + + $items = []; + + $index++; + + while ($index < $numLines) { + $item = $line($index); + + if (preg_match(PRE, $item, $matches) === 1) { + break; + } + + $items[] = $item; + + $index++; + } + + $tokens[] = [ + 'type' => PRE, + 'alt' => $alt, + 'items' => $items, + ]; + } elseif (preg_match(QUOTE, $line($index), $matches) === 1) { + [, $content] = $matches; + + $tokens[] = [ + 'type' => QUOTE, + 'content' => $content, + ]; + } else { + $tokens[] = [ + 'type' => null, + 'content' => $line($index), + ]; + } + + $index++; + } + + return $tokens; +} + +function renderGemtextTokens(array $tokens): string +{ + return implode("\n", array_filter(array_map(function ($token) { + switch ($token['type']) { + case HEADER: + return sprintf('<h%s>%s</h%s>', $token['level'], $token['content'], $token['level']); + case LINK: + return sprintf('<a href="%s">%s</a>', $token['href'], $token['content']); + case LIST_ITEM: + return sprintf( + "<ul>\n%s\n</ul>", + implode("\n", array_map(function ($item) { return sprintf("<li>%s</li>", $item);}, $token['items'])) + ); + case PRE: + return $token['alt'] ? + sprintf("<pre><code class=\"%s\">\n%s\n</code></pre>", $token['alt'], implode("\n", $token['items'])) + : + sprintf("<pre>\n%s\n</pre>", $token['alt'], implode('', $token['items'])); + case QUOTE: + return sprintf('<blockquote>%s</blockquote>', $token['content']); + default: + return $token['content'] !== '' ? sprintf('<p>%s</p>', $token['content']) : null; + } + }, $tokens))); +} + +function copyWWWAssets(string $assetsDiretory, string $output): void +{ + $assetsDiretory = realpath($assetsDiretory); + + $iter = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($assetsDiretory, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::SELF_FIRST + ); + + foreach ($iter as $fileInfo) { + if (!is_file($fileInfo->getRealPath())) { + continue; + } + + $input = $fileInfo->getRealPath(); + $url = str_replace($assetsDiretory, '', $input); + + $destFile = $output.DIRECTORY_SEPARATOR.$url; + + $destDirectory = dirname($destFile); + + if (!file_exists($destDirectory) && !mkdir($destDirectory, 0777, true)) { + echo "Unable to create WWW asset directory $destDirectory."; + exit(1); + } + + copy($input, $destFile); + } +} + diff --git a/www_assets/images/favicon.png b/www_assets/images/favicon.png Binary files differnew file mode 100644 index 0000000..4909792 --- /dev/null +++ b/www_assets/images/favicon.png |
