# How the Star Rating System Works in HowToCook: A Complete Technical Guide

> Uncover the technical workings of the HowToCook star rating system. Learn how Unicode stars and countStars function create recipe difficulty categories and navigation.

- Repository: [Anduin Xue/HowToCook](https://github.com/Anduin2017/HowToCook)
- Tags: deep-dive
- Published: 2026-02-27

---

**The HowToCook star rating system automatically categorizes recipes by difficulty using Unicode star characters (★) embedded in markdown files, then generates indexed navigation pages through the `countStars()` function in [`.github/readme-generate.js`](https://github.com/Anduin2017/HowToCook/blob/main/.github/readme-generate.js).**

The Anduin2017/HowToCook repository implements a data-driven star rating system to help cooks find recipes matching their skill level. This automated pipeline parses difficulty ratings from individual recipe files and generates organized index pages without requiring manual maintenance.

## How the Star Rating System Parses Recipe Difficulty

### The Star Syntax in Recipe Markdown Files

Every recipe in the repository includes a difficulty declaration using Unicode star characters. The line appears as:

```markdown
预估烹饪难度：★

```

The number of ★ symbols ranges from 1 to 5, representing increasing complexity. For example, the *鸡蛋花* recipe contains a single star, indicating beginner-friendly difficulty, located at line 5 in `dishes/vegetable_dish/鸡蛋花/鸡蛋花.md`.

### Counting Stars with the countStars() Function

The core parsing logic resides in [`.github/readme-generate.js`](https://github.com/Anduin2017/HowToCook/blob/main/.github/readme-generate.js) at lines 65-73. The `countStars()` function reads markdown files and tallies star occurrences:

```javascript
async function countStars(filename) {
  const data = await fs.readFile(filename, 'utf-8');
  let stars = 0;
  const lines = data.split('\n');
  lines.forEach(line => {
    stars += (line.match(/★/g) || []).length;
  });
  return stars;
}

```

This implementation uses a regular expression to match all ★ characters across the entire file, ensuring accurate difficulty calculation even if formatting varies.

## Generating the Star System Index Pages

### Organizing Recipes by Difficulty

The `organizeByStars()` function (lines 75-90 in [`.github/readme-generate.js`](https://github.com/Anduin2017/HowToCook/blob/main/.github/readme-generate.js)) orchestrates the categorization process. It traverses the `dishes/` directory, invokes `countStars()` for every `.md` file, and constructs a mapping of file paths to their respective star counts.

### Creating Star-Specific Markdown Files

After collecting the difficulty data, the generator creates individual index pages within the `starsystem/` directory. Files follow the naming convention `{N}Star.md`, ranging from [`0Star.md`](https://github.com/Anduin2017/HowToCook/blob/main/0Star.md) to [`5Star.md`](https://github.com/Anduin2017/HowToCook/blob/main/5Star.md).

Each generated file contains a header and bulleted list of recipes with relative links:

```markdown

# 1 星难度菜品

* [鸡蛋花](../dishes/vegetable_dish/鸡蛋花/鸡蛋花.md)

```

This structure enables direct navigation to recipes matching specific skill levels.

## Integrating Star Ratings into the Main README

The automation extends to the repository's main entry point. The script constructs a navigation block:

```markdown

### 按难度索引

- [0 星难度](starsystem/0Star.md)
- [1 星难度](starsystem/1Star.md)

```

This block replaces the `{{index_stars}}` placeholder in [`README_template.md`](https://github.com/Anduin2017/HowToCook/blob/main/README_template.md), resulting in the live "按难度索引" section visible in [`README.md`](https://github.com/Anduin2017/HowToCook/blob/main/README.md) (lines 55-59). The process ensures the difficulty navigation stays synchronized with the actual recipe collection without manual updates.

## Practical Code Examples

### Checking a Specific Recipe's Rating

Use the `countStars` function directly to verify a dish's difficulty:

```javascript
const { countStars } = require('./.github/readme-generate.js');

(async () => {
  const stars = await countStars('dishes/vegetable_dish/鸡蛋花/鸡蛋花.md');
  console.log(`鸡蛋花 rating: ${stars} ★`);
})();

```

### Regenerating the Entire Star System

Execute the full pipeline from the repository root:

```bash
node .github/readme-generate.js

```

This command rescans all dishes, rebuilds the `starsystem/` index pages, and updates [`README.md`](https://github.com/Anduin2017/HowToCook/blob/main/README.md) with current navigation links.

### Reading Generated Star Indexes Programmatically

Access the pre-built difficulty lists for external applications:

```javascript
const fs = require('fs').promises;

async function listStars(star) {
  const file = `starsystem/${star}Star.md`;
  const content = await fs.readFile(file, 'utf-8');
  console.log(`Recipes with ${star} star(s):\n${content}`);
}

listStars(3);   // prints all 3‑star recipes

```

## Summary

- The **star rating system** in HowToCook uses Unicode ★ characters embedded in recipe markdown to indicate cooking difficulty from 1 to 5 stars.
- The **`countStars()`** function in [`.github/readme-generate.js`](https://github.com/Anduin2017/HowToCook/blob/main/.github/readme-generate.js) parses these characters using regex matching to calculate difficulty scores.
- The **`organizeByStars()`** function automatically categorizes recipes and generates dedicated index pages ([`0Star.md`](https://github.com/Anduin2017/HowToCook/blob/main/0Star.md) through [`5Star.md`](https://github.com/Anduin2017/HowToCook/blob/main/5Star.md)) in the `starsystem/` directory.
- The README navigation updates automatically via template replacement of the `{{index_stars}}` placeholder, ensuring the difficulty index remains synchronized with the recipe collection.

## Frequently Asked Questions

### How is the cooking difficulty determined for each recipe?

Recipe authors manually specify difficulty by including a line such as `预估烹饪难度：★★★` in the markdown file. The number of ★ symbols (1 to 5) represents the estimated complexity, preparation time, and technique required. The generator script does not assign ratings automatically; it only reads the existing markers.

### Can I filter recipes by star rating on the GitHub website?

Yes. The repository generates static markdown index pages in the `starsystem/` folder (e.g., [`1Star.md`](https://github.com/Anduin2017/HowToCook/blob/main/1Star.md), [`2Star.md`](https://github.com/Anduin2017/HowToCook/blob/main/2Star.md)). You can navigate to these files directly in the GitHub file browser or through the "按难度索引" (Index by Difficulty) section in the main README, which links to each star category.

### What happens if a recipe has no star rating or an invalid number of stars?

If a recipe contains no ★ characters, the `countStars()` function returns 0, and the recipe is categorized under [`0Star.md`](https://github.com/Anduin2017/HowToCook/blob/main/0Star.md). The current implementation does not enforce a maximum limit during counting, but the convention limits ratings to 5 stars. Recipes with more than 5 stars would still be counted accurately but would generate index files like [`6Star.md`](https://github.com/Anduin2017/HowToCook/blob/main/6Star.md) if they existed.

### How do I update the star rating for an existing recipe?

Edit the specific recipe markdown file in the `dishes/` directory and modify the line containing `预估烹饪难度：`. Change the number of ★ symbols to reflect the new difficulty level. After committing the change, run `node .github/readme-generate.js` to regenerate the star system indexes and update the README navigation automatically.