BuildCommand
Leaf\BuildCommand is the build pipeline under the hood. It handles everything from rendering pages to generating SEO files. Both leaf build (binary CLI) and composer build (Composer template) call it.
Running a build
Binary CLI
leaf build
That's it. No PHP code to write, no bin/build.php to maintain.
Composer template
The template ships a bin/build.php that you own:
composer build
# or
php bin/build.php
// bin/build.php
define('ROOT_DIR', dirname(__DIR__));
require ROOT_DIR . '/vendor/autoload.php';
use App\Models\Core\Application;
use Leaf\BuildCommand;
$app = new Application();
$command = new BuildCommand($app);
exit($command->run());
Edit this file to customize the build.
What the pipeline does
- Discovers all GET routes from your controllers (Composer path) or scaffold routes (CLI path)
- Scans
content/for Markdown files and adds their paths - Renders each page through the full Zephyrus application stack
- Copies static assets from
public/todist/ - Moves
/404/index.htmlto/404.html - Generates
search.jsonfor client-side search - Creates a root redirect (single-locale) or builds default locale to root (multi-locale). If a
GET /route exists (custom landing), it's rendered instead of the redirect. - Generates
sitemap.xmlandrobots.txt(ifproduction_urlis configured) - Runs any registered post-build callbacks
Customizing (Composer path only)
If you're on the CLI path, skip this section — the leaf binary drives a stock pipeline. To customize, leaf eject first.
Adding custom paths
For parameterized routes the router can't auto-discover (like /blog/{slug}):
$command = new BuildCommand($app);
$command->addPaths([
'/blog',
'/blog/my-first-post',
'/blog/another-post',
]);
exit($command->run());
Excluding paths
$command->excludePatterns([
'#^/api/#', // Exclude API routes
'#^/admin#', // Exclude admin pages
]);
/search.json is always excluded (generated separately). / is excluded when no custom GET / route exists (handled by the redirect/default-locale logic).
Post-build hooks
$command->onPostBuild(function ($result, $outputDir) {
// Generate OG images
passthru('node bin/generate-og-images.js');
// Optimize images
passthru('npx imagemin dist/assets/images/* --out-dir=dist/assets/images');
echo "Post-build complete!" . PHP_EOL;
});
The callback receives:
$result— aStaticBuildResultwithpagesBuilt,totalPaths,errors,builtPages$outputDir— absolute path to the output directory
Build output
A successful build produces:
dist/
index.html # Root page (or locale redirect)
404.html # Error page
search.json # Search index
sitemap.xml # XML sitemap (if production_url set)
robots.txt # Robots file (if production_url set)
assets/ # CSS, JS, images
getting-started/
introduction/
index.html # Each page becomes a directory with index.html