Skip to content

Commit bd3bace

Browse files
authored
refactor(build): nest static-site generator under cook build web (#348)
* refactor(build): nest static-site generator under `cook build web` Restructures the `cook build` command as a parent that dispatches to named subcommands, with `web` as the first (and currently only) target. This carves out room for future build outputs (e.g. printable cookbooks) without further reshaping the CLI surface. Breaking change: `cook build` invocations must now specify the target, e.g. `cook build web` instead of `cook build`. * test: update help-output snapshots for new build short description
1 parent 6aa725c commit bd3bace

8 files changed

Lines changed: 74 additions & 27 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,19 +272,19 @@ cook server --port 8080
272272
cook server --open
273273
```
274274
275-
### `cook build`
275+
### `cook build web`
276276
277277
Generate a self-contained static website from your recipes. Hostable anywhere or browsable via `file://`.
278278
279279
```bash
280280
# Build into ./_site from the current directory
281-
cook build
281+
cook build web
282282
283283
# Build a specific collection into a custom output
284-
cook build dist --base-path ~/my-recipes
284+
cook build web dist --base-path ~/my-recipes
285285
286286
# Build for hosting under a subpath
287-
cook build --base-url /recipes/
287+
cook build web --base-url /recipes/
288288
```
289289
290290
### `cook search`

docs/build.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
# Build Command
22

3+
The `cook build` command groups artifact-generation subcommands. Today it offers `web` for static-site generation; future targets (e.g. cookbooks) will live alongside it.
4+
5+
## `cook build web`
6+
37
Generate a self-contained static website from your recipe collection. The output mirrors `cook server`'s browsing experience but ships as plain HTML, CSS, and JS — no Rust process needed at runtime, so it can be hosted on GitHub Pages, Netlify, S3, or opened directly via `file://`.
48

59
## Usage
610

711
```
8-
cook build [OPTIONS] [OUTPUT_DIR]
12+
cook build web [OPTIONS] [OUTPUT_DIR]
913
```
1014

1115
## Arguments
@@ -25,13 +29,13 @@ cook build [OPTIONS] [OUTPUT_DIR]
2529

2630
```bash
2731
# Build into ./_site from the current directory
28-
cook build
32+
cook build web
2933

3034
# Build a specific recipe collection into a custom output directory
31-
cook build dist --base-path ~/my-recipes
35+
cook build web dist --base-path ~/my-recipes
3236

3337
# Build for hosting under /recipes/ on your domain
34-
cook build --base-url /recipes/
38+
cook build web --base-url /recipes/
3539
```
3640

3741
## What gets generated
@@ -66,7 +70,7 @@ Because internal links default to page-relative paths, no configuration is neede
6670

6771
```bash
6872
# GitHub Pages: push _site/ to gh-pages
69-
cook build && git -C _site init && git -C _site add . && \
73+
cook build web && git -C _site init && git -C _site add . && \
7074
git -C _site commit -m "site" && \
7175
git -C _site push -f git@github.com:user/repo gh-pages
7276

@@ -85,5 +89,5 @@ Use `--base-url` only if your host serves the site under a fixed subpath and you
8589

8690
- The generated site has no server dependency — it works fully offline via `file://`.
8791
- Search runs entirely in the browser by loading `static/search-index.json`.
88-
- Re-run `cook build` after editing recipes; the command is idempotent.
92+
- Re-run `cook build web` after editing recipes; the command is idempotent.
8993
- For a live editing experience, use `cook server` instead.

src/args.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,18 @@ pub enum Command {
8787
)]
8888
Server(server::ServerArgs),
8989

90-
/// Generate a self-contained static website from your recipe collection
90+
/// Build artifacts from your recipe collection
9191
///
92-
/// Renders your recipes as static HTML files browsable on any static-file
93-
/// host or directly from disk via file://. Excludes dynamic features
94-
/// (shopping list, pantry, editing).
92+
/// Container for build subcommands. Currently supports `web` (static
93+
/// website generation). More targets (e.g. cookbooks) may be added in
94+
/// future releases.
9595
///
9696
/// Examples:
97-
/// cook build # Build to ./_site
98-
/// cook build out # Build to ./out
99-
/// cook build --base-path ~/recipes # Use specific source directory
100-
/// cook build --base-url /recipes/ # Absolute URL prefix for subpath hosting
101-
#[command(long_about = "Generate a static HTML website from your recipe collection")]
97+
/// cook build web # Build a static website to ./_site
98+
/// cook build web out # Build to ./out
99+
/// cook build web --base-path ~/recipes # Use specific source directory
100+
/// cook build web --base-url /recipes/ # Absolute URL prefix for subpath hosting
101+
#[command(long_about = "Build artifacts (static website, etc.) from your recipe collection")]
102102
Build(build::BuildArgs),
103103

104104
/// Generate a combined shopping list from multiple recipes

src/build/mod.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,33 @@ use crate::util::resolve_to_absolute_path;
88
use crate::Context;
99
use anyhow::{bail, Context as _, Result};
1010
use camino::Utf8PathBuf;
11-
use clap::Args;
11+
use clap::{Args, Subcommand};
1212
use unic_langid::LanguageIdentifier;
1313

1414
#[derive(Debug, Args)]
1515
pub struct BuildArgs {
16+
#[command(subcommand)]
17+
pub command: BuildCommand,
18+
}
19+
20+
#[derive(Debug, Subcommand)]
21+
pub enum BuildCommand {
22+
/// Generate a self-contained static website from your recipes
23+
///
24+
/// Renders your recipes as static HTML files browsable on any static-file
25+
/// host or directly from disk via file://. Excludes dynamic features
26+
/// (shopping list, pantry, editing).
27+
///
28+
/// Examples:
29+
/// cook build web # Build to ./_site
30+
/// cook build web out # Build to ./out
31+
/// cook build web --base-path ~/recipes # Use specific source directory
32+
/// cook build web --base-url /recipes/ # Absolute URL prefix for subpath hosting
33+
Web(WebBuildArgs),
34+
}
35+
36+
#[derive(Debug, Args)]
37+
pub struct WebBuildArgs {
1638
/// Output directory for the generated static site
1739
///
1840
/// Defaults to ./_site if not specified. The directory is created if
@@ -51,11 +73,19 @@ fn parse_lang_arg(s: &str) -> Result<LanguageIdentifier, String> {
5173

5274
impl BuildArgs {
5375
pub fn get_base_path(&self) -> Option<Utf8PathBuf> {
54-
self.base_path.clone()
76+
match &self.command {
77+
BuildCommand::Web(args) => args.base_path.clone(),
78+
}
5579
}
5680
}
5781

5882
pub fn run(ctx: &Context, args: BuildArgs) -> Result<()> {
83+
match args.command {
84+
BuildCommand::Web(web_args) => run_web(ctx, web_args),
85+
}
86+
}
87+
88+
fn run_web(ctx: &Context, args: WebBuildArgs) -> Result<()> {
5989
let source = resolve_to_absolute_path(ctx.base_path())?;
6090
if !source.is_dir() {
6191
bail!("Source base path is not a directory: {source}");
@@ -82,7 +112,7 @@ pub fn run(ctx: &Context, args: BuildArgs) -> Result<()> {
82112
let mut tree = cooklang_find::build_tree(&source)
83113
.map_err(|e| anyhow::anyhow!("Failed to build recipe tree: {e}"))?;
84114
// If the user pointed the output directory inside the source directory
85-
// (the common case: `cook build` with default `_site` next to recipes),
115+
// (the common case: `cook build web` with default `_site` next to recipes),
86116
// strip the output subtree so we don't re-process the previous run's
87117
// generated files. Without this, every run would nest `_site/recipe/...`
88118
// and `_site/api/static/...` one level deeper until the OS rejects the

src/server/builders.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! struct ready to render (or be turned into an `axum::Response` by a handler).
44
//!
55
//! The builders intentionally avoid any axum / tokio-async types so they can be
6-
//! reused from a non-async context (e.g. `cook build`).
6+
//! reused from a non-async context (e.g. `cook build web`).
77
88
use crate::server::templates::*;
99
use anyhow::Result;

tests/build.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fn seed_dir() -> PathBuf {
99
#[test]
1010
fn build_command_help_works() {
1111
let mut cmd = Command::cargo_bin("cook").unwrap();
12-
cmd.args(["build", "--help"]).assert().success();
12+
cmd.args(["build", "web", "--help"]).assert().success();
1313
}
1414

1515
#[test]
@@ -21,6 +21,7 @@ fn build_creates_output_dir() {
2121
let mut cmd = Command::cargo_bin("cook").unwrap();
2222
cmd.args([
2323
"build",
24+
"web",
2425
out.to_str().unwrap(),
2526
"--base-path",
2627
seed.to_str().unwrap(),
@@ -41,6 +42,7 @@ fn build_writes_index_and_static_assets() {
4142
.unwrap()
4243
.args([
4344
"build",
45+
"web",
4446
out.to_str().unwrap(),
4547
"--base-path",
4648
seed.to_str().unwrap(),
@@ -75,6 +77,7 @@ fn build_lang_arg_changes_ui_locale() {
7577
.unwrap()
7678
.args([
7779
"build",
80+
"web",
7881
out.to_str().unwrap(),
7982
"--base-path",
8083
seed.to_str().unwrap(),
@@ -103,6 +106,7 @@ fn build_lang_arg_rejects_unsupported() {
103106
.unwrap()
104107
.args([
105108
"build",
109+
"web",
106110
out.to_str().unwrap(),
107111
"--base-path",
108112
seed.to_str().unwrap(),
@@ -123,6 +127,7 @@ fn build_writes_recipe_pages() {
123127
.unwrap()
124128
.args([
125129
"build",
130+
"web",
126131
out.to_str().unwrap(),
127132
"--base-path",
128133
seed.to_str().unwrap(),
@@ -194,6 +199,7 @@ fn build_renders_recipes_with_title_metadata() {
194199
.unwrap()
195200
.args([
196201
"build",
202+
"web",
197203
out.to_str().unwrap(),
198204
"--base-path",
199205
seed.to_str().unwrap(),
@@ -223,6 +229,7 @@ fn build_writes_search_index() {
223229
.unwrap()
224230
.args([
225231
"build",
232+
"web",
226233
out.to_str().unwrap(),
227234
"--base-path",
228235
seed.to_str().unwrap(),
@@ -266,6 +273,7 @@ fn build_copies_images_when_present() {
266273
.unwrap()
267274
.args([
268275
"build",
276+
"web",
269277
out.to_str().unwrap(),
270278
"--base-path",
271279
seed.to_str().unwrap(),
@@ -291,6 +299,7 @@ fn build_writes_search_js() {
291299
.unwrap()
292300
.args([
293301
"build",
302+
"web",
294303
out.to_str().unwrap(),
295304
"--base-path",
296305
seed.to_str().unwrap(),
@@ -339,6 +348,7 @@ fn build_writes_menu_pages_without_dotmenu_suffix() {
339348
.unwrap()
340349
.args([
341350
"build",
351+
"web",
342352
out.to_str().unwrap(),
343353
"--base-path",
344354
seed.to_str().unwrap(),
@@ -368,6 +378,7 @@ fn static_output_omits_dynamic_ui() {
368378
.unwrap()
369379
.args([
370380
"build",
381+
"web",
371382
out.to_str().unwrap(),
372383
"--base-path",
373384
seed.to_str().unwrap(),
@@ -415,6 +426,7 @@ fn build_internal_links_resolve_to_existing_files() {
415426
.unwrap()
416427
.args([
417428
"build",
429+
"web",
418430
out.to_str().unwrap(),
419431
"--base-path",
420432
seed.to_str().unwrap(),
@@ -449,7 +461,7 @@ fn build_internal_links_resolve_to_existing_files() {
449461
#[test]
450462
fn build_twice_with_output_inside_source_does_not_recurse() {
451463
// Regression: when the output dir lives inside the source dir
452-
// (`cook build` from the recipe root with default `_site`), every run
464+
// (`cook build web` from the recipe root with default `_site`), every run
453465
// used to discover the previous run's generated files and copy them one
454466
// level deeper, eventually hitting ENAMETOOLONG.
455467
let tmp = TempDir::new().unwrap();
@@ -477,6 +489,7 @@ fn build_twice_with_output_inside_source_does_not_recurse() {
477489
.unwrap()
478490
.args([
479491
"build",
492+
"web",
480493
out.to_str().unwrap(),
481494
"--base-path",
482495
source.to_str().unwrap(),

tests/snapshots/snapshot_test__help_output.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Usage: cook [OPTIONS] <COMMAND>
1010
Commands:
1111
recipe Parse, validate and display recipe files in various formats
1212
server Start a local web server to browse and view your recipe collection
13-
build Generate a self-contained static website from your recipe collection
13+
build Build artifacts from your recipe collection
1414
shopping-list Generate a combined shopping list from multiple recipes [aliases: sl]
1515
seed Initialize a directory with example Cooklang recipes
1616
search Search through your recipe collection for matching text

tests/snapshots/snapshot_test__help_output_no_update.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Usage: cook [OPTIONS] <COMMAND>
1010
Commands:
1111
recipe Parse, validate and display recipe files in various formats
1212
server Start a local web server to browse and view your recipe collection
13-
build Generate a self-contained static website from your recipe collection
13+
build Build artifacts from your recipe collection
1414
shopping-list Generate a combined shopping list from multiple recipes [aliases: sl]
1515
seed Initialize a directory with example Cooklang recipes
1616
search Search through your recipe collection for matching text

0 commit comments

Comments
 (0)