Harbor

branch main
showing the latest snapshot on main
build.php 4.2 KB · PHP
build.php 0644 Raw
#!/usr/bin/env php
<?php

declare(strict_types=1);

require __DIR__ . '/src/helpers.php';

if (in_array('--help', $argv, true) || in_array('-h', $argv, true)) {
	echo "Usage: php build.php\n";
	echo "Builds static HTML from src/ into public/ with no external dependencies.\n";
	exit(0);
}

$root = __DIR__;
$sourceRoot = $root . '/src';
$outputRoot = $root . '/public';

$site = require $sourceRoot . '/site.php';
$pages = [];

foreach (glob($sourceRoot . '/pages/*.php') ?: [] as $pageFile) {
	$page = require $pageFile;
	if (!is_array($page)) {
		throw new RuntimeException("Page file {$pageFile} must return an array.");
	}

	$pages[] = $page;
}

foreach ($site['projects'] as $index => $project) {
	$pages[] = [
		'key' => $project['route'],
		'output' => $project['output'],
		'title' => $project['title'] . ' | Sharkk',
		'description' => $project['summary'],
		'template' => 'project',
		'order' => 100 + $index,
		'project' => $project,
		'slider' => [
			'class' => 'work-header',
		],
	];
}

foreach ($site['blogPosts'] as $index => $post) {
	$pages[] = [
		'key' => $post['route'],
		'output' => $post['output'],
		'title' => $post['title'] . ' | Sharkk',
		'description' => $post['description'],
		'template' => 'blog-post',
		'order' => 200 + $index,
		'post' => $post,
	];
}

$routes = [];
$outputs = [];

foreach ($pages as $page) {
	foreach (['key', 'output', 'title', 'template'] as $field) {
		if (!isset($page[$field]) || $page[$field] === '') {
			throw new RuntimeException("Every page must define a non-empty {$field} field.");
		}
	}

	if (isset($routes[$page['key']])) {
		throw new RuntimeException("Duplicate route key: {$page['key']}");
	}

	$output = normalize_site_path($page['output']);
	if (isset($outputs[$output])) {
		throw new RuntimeException("Duplicate output path: {$output}");
	}

	$page['output'] = $output;
	$routes[$page['key']] = $page;
	$outputs[$output] = true;
}

usort($pages, static fn (array $a, array $b): int => ($a['order'] ?? 999) <=> ($b['order'] ?? 999));

$renderedPages = [];

foreach ($pages as $page) {
	$currentOutput = $page['output'];
	$link = static function (string $routeKey) use ($routes, $currentOutput): string {
		if (!isset($routes[$routeKey])) {
			throw new RuntimeException("Unknown route key: {$routeKey}");
		}

		return relative_url($currentOutput, $routes[$routeKey]['output']);
	};
	$asset = static fn (string $path): string => relative_url($currentOutput, normalize_site_path($path));

	$renderPartial = null;
	$shared = [
		'site' => $site,
		'page' => $page,
		'routes' => $routes,
		'link' => $link,
		'asset' => $asset,
	];

	$renderPartial = static function (string $name, array $vars = []) use ($sourceRoot, $shared, &$renderPartial): string {
		return render_template(
			$sourceRoot . '/templates/partials/' . normalize_site_path($name) . '.php',
			array_merge($shared, $vars, ['renderPartial' => $renderPartial])
		);
	};

	$bodyHtml = $renderPartial($page['template']);
	$html = render_template($sourceRoot . '/templates/layout.php', array_merge($shared, [
		'bodyHtml' => $bodyHtml,
		'renderPartial' => $renderPartial,
	]));

	$renderedPages[] = [
		'page' => $page,
		'html' => $html,
	];
}

foreach ($renderedPages as $renderedPage) {
	$outputPath = join_site_path($outputRoot, $renderedPage['page']['output']);
	$outputDir = dirname($outputPath);

	if (!is_dir($outputDir) && !mkdir($outputDir, 0777, true) && !is_dir($outputDir)) {
		throw new RuntimeException("Unable to create output directory: {$outputDir}");
	}

	if (file_put_contents($outputPath, $renderedPage['html']) === false) {
		throw new RuntimeException("Unable to write output file: {$outputPath}");
	}
}

$blogIndexPath = join_site_path($outputRoot, 'blog/search-index.json');
$encodedBlogIndex = json_payload(blog_search_index($site['blogPosts']), true);

if (file_put_contents($blogIndexPath, $encodedBlogIndex . "\n") === false) {
	throw new RuntimeException("Unable to write blog search index: {$blogIndexPath}");
}

foreach ($renderedPages as $renderedPage) {
	validate_local_references($outputRoot, $renderedPage['page']['output'], $renderedPage['html']);
}

echo 'Built ' . count($renderedPages) . " pages into public/:\n";
foreach ($renderedPages as $renderedPage) {
	echo ' - ' . $renderedPage['page']['output'] . "\n";
}