HowToCook Project Structure and Organization: A Technical Deep Dive
The HowToCook repository employs a content-first architecture where Markdown recipes reside in categorized directories under dishes/ and tips/, while a Node.js automation script generates navigation indexes, difficulty ratings, and static-site configuration automatically.
The Anduin2017/HowToCook project demonstrates how a simple HowToCook project structure can scale to hundreds of recipes without manual maintenance. By storing culinary content as plain Markdown and leveraging a lightweight build pipeline, the repository maintains a clean, contributor-friendly organization while automatically generating complex navigation systems and difficulty classifications.
Repository Layout and Directory Structure
The repository organizes content, automation, and generated artifacts into distinct top-level directories. This separation ensures that contributors can focus on writing recipes while the build system handles navigation and formatting.
Core Content Directories
All human-authored content lives in two primary locations:
-
dishes/– The main recipe collection organized by culinary category. Each subfolder represents a category (e.g.,meat_dish/,vegetable_dish/,breakfast/,soup/) and contains individual dish folders with Markdown files and optional images. For example,dishes/meat_dish/黑椒牛柳/黑椒牛柳.mdcontains the recipe for Black Pepper Beef Tenderloin. -
tips/– Supplementary cooking knowledge including equipment guides, kitchen safety protocols, and advanced techniques. Files liketips/厨房准备.mdprovide foundational knowledge that supports the recipe collection.
Generated Navigation and Configuration Files
The HowToCook project structure relies on auto-generated files that should never be edited manually:
-
starsystem/– Contains difficulty index files (0Star.mdthrough5Star.md) generated by counting Unicode star characters (★) in each recipe. These files are completely auto-generated based on star counts in recipe files. -
README.md– The repository's human-readable entry point. This file is regenerated automatically to include categorized recipe lists, difficulty indexes, and contribution guidelines. -
mkdocs.yml– Configuration file for the MkDocs static site generator, automatically updated to reflect the current directory structure for web deployment.
Build System and Templates
Automation logic and contributor onboarding resources reside in:
-
.github/readme-generate.js– The core Node.js script that orchestrates the entire build process, from scanning directories to rendering final output files. -
dishes/template/示例菜/示例菜.md– The standardized recipe template that contributors copy when adding new dishes. This template includes commented sections for ingredients, measurements, cooking steps, and difficulty ratings. -
.github/workflows/build.yml– GitHub Actions configuration that executes the build pipeline on every push to ensure documentation remains synchronized with content changes. -
package.json– Declares Node.js runtime dependencies and build scripts, including linting tools liketextlintandmarkdownlint-cli.
How the Build Automation Works
The repository maintains its HowToCook project structure through a lightweight but powerful Node.js automation pipeline. This system eliminates manual maintenance of navigation files while ensuring consistency across hundreds of recipes.
The readme-generate.js Script
Located at .github/readme-generate.js, this script serves as the central orchestrator for repository maintenance. It performs four critical operations without requiring external services or databases:
-
Directory Scanning – The
getAllMarkdown()function recursively traverses the repository tree, excludingnode_modulesand.gitdirectories, to collect every.mdfile with its relative path. -
Content Classification – The script categorizes each file as either a tip (
tips/), advanced tip (tips/advanced/), or specific dish category based on parent folder names (e.g.,meat_dish/,vegetable_dish/). -
Star Rating Calculation – The
organizeByStars()function processes each recipe to count difficulty indicators. -
Template Rendering – The script populates
readme_template.mdandmkdocs_template.ymlwith generated navigation sections, writing the final files toREADME.mdandmkdocs.yml.
Scanning and Classification Logic
The build system distinguishes between different content types by analyzing file paths. During the scanning phase, the script builds a categories object that groups files into logical sections:
- Recipes in
dishes/breakfast/populate the breakfast category - Files in
tips/advanced/populate the advanced techniques section - Each category maintains an array of file objects containing paths and metadata
This classification enables the template renderer to generate hierarchical navigation without hardcoding directory names.
Star Rating Index Generation
The difficulty classification system relies on the countStars() function to analyze recipe complexity:
async function countStars(filename) {
const data = await fs.readFile(filename, 'utf-8');
let stars = 0;
const lines = data.split('\n');
lines.forEach(line => {
// Count every ★ character in the file
stars += (line.match(/★/g) || []).length;
});
return stars;
}
When organizeByStars() processes the recipe collection, it aggregates files by their star count (0 through 5) and generates corresponding Markdown files in starsystem/. A recipe containing three stars appears in starsystem/3Star.md, while five-star recipes populate 5Star.md, creating a browsable difficulty ladder.
CI/CD Pipeline Integration
The .github/workflows/build.yml configuration ensures the HowToCook project structure remains synchronized automatically:
# Install dev dependencies (Node.js ≥ 14)
npm install
# Re-generate README and MkDocs files
npm run build
Every push to the repository triggers the GitHub Actions workflow, which executes readme-generate.js. If the script generates changes to README.md, mkdocs.yml, or star system files, the CI pipeline commits these updates automatically, ensuring navigation always reflects the current content state without manual intervention.
Adding a New Recipe to the Repository
Contributors interact with the HowToCook project structure through a standardized workflow that requires no knowledge of the build system. The process leverages the template system and relies on CI automation for integration.
Follow these steps to add a new dish:
# 1️⃣ Copy the template
cp -r dishes/template/示例菜 dishes/meat_dish/新菜名
# 2️⃣ Rename the Markdown file (keep the same name as the dish)
mv dishes/meat_dish/新菜名/示例菜.md dishes/meat_dish/新菜名/新菜名.md
# 3️⃣ Edit the file, delete the <!-- … --> comments and fill the sections
# (ingredients, calculations, steps, difficulty ★ rating, etc.)
# 4️⃣ Commit & push – CI will rebuild README & star index automatically
git add dishes/meat_dish/新菜名
git commit -m "Add 新菜名 recipe"
git push
Because the build script recursively scans the dishes/ tree, the new recipe automatically appears in:
- The category list in
README.mdunder the appropriate section (e.g., "荤菜" for meat dishes) - The difficulty index (
starsystem/XStar.md) based on the number of ★ characters in the file - The MkDocs navigation via the auto-generated
mkdocs.ymlconfiguration
Summary
- The HowToCook project structure maintains strict separation between human-authored content in
dishes/andtips/and machine-generated navigation inREADME.md,starsystem/, andmkdocs.yml. - A single Node.js script (
.github/readme-generate.js) automates all maintenance tasks, usinggetAllMarkdown()for directory scanning,organizeByStars()for difficulty classification, and template rendering for output generation. - Recipes use a standardized Markdown template with Unicode star characters (★) to indicate difficulty, which the build system counts via
countStars()to generate browsable difficulty indexes. - GitHub Actions CI executes
npm run buildon every push, ensuring navigation files remain synchronized with content changes without manual intervention.
Frequently Asked Questions
What programming language powers the HowToCook build system?
The build automation is written in JavaScript (Node.js). The main script .github/readme-generate.js uses native Node.js modules such as fs/promises and path to scan directories, parse Markdown files, and generate static navigation files. The project requires no external build tools like Jekyll or Hugo, relying only on Node.js and a few dev-dependencies including textlint and markdownlint-cli.
How does the repository automatically categorize recipes by difficulty?
The system uses the countStars() function within readme-generate.js to scan each recipe file for Unicode star characters (★). It totals the count for each file and aggregates recipes into difficulty-specific Markdown files located in starsystem/ (e.g., 3Star.md for three-star recipes). This creates a browsable difficulty ladder ranging from 0 stars (simple) to 5 stars (complex) without requiring manual categorization by contributors.
Can I contribute a new recipe without manually editing the README?
Yes. Contributors only need to copy the template from dishes/template/示例菜/示例菜.md to the appropriate category folder (such as dishes/meat_dish/), fill in the recipe content including the difficulty rating using ★ characters, and commit the changes. The GitHub Actions workflow defined in .github/workflows/build.yml automatically runs npm run build, which executes readme-generate.js to regenerate README.md, mkdocs.yml, and the star rating indexes to include your new recipe.
What is the purpose of the mkdocs.yml file in the repository?
The mkdocs.yml file serves as the configuration for MkDocs, a static site generator that publishes the cookbook as a browsable website. It is auto-generated by the build script to reflect the current directory structure of recipes and tips, mapping Markdown content to navigation menus. This ensures the web version of the cookbook stays synchronized with the repository structure without requiring manual updates to the site configuration when new categories or recipes are added.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →