{"id":15037125,"url":"https://github.com/anduin2017/howtocook","last_synced_at":"2026-02-16T04:01:09.282Z","repository":{"id":36973067,"uuid":"243950408","full_name":"Anduin2017/HowToCook","owner":"Anduin2017","description":"程序员在家做饭方法指南。Programmer's guide about how to cook at home (Simplified Chinese only).","archived":false,"fork":false,"pushed_at":"2025-12-05T10:04:17.000Z","size":465132,"stargazers_count":96234,"open_issues_count":459,"forks_count":10714,"subscribers_count":488,"default_branch":"master","last_synced_at":"2025-12-08T01:18:19.466Z","etag":null,"topics":["chinese","cookbook","cooking","dishes","recipes"],"latest_commit_sha":null,"homepage":"https://cook.aiursoft.com","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Anduin2017.png","metadata":{"files":{"readme":".github/readme-generate.js","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-02-29T10:43:49.000Z","updated_at":"2025-12-08T00:43:16.000Z","dependencies_parsed_at":"2023-10-16T03:39:07.171Z","dependency_job_id":"2d28ed6f-1128-4fa3-b161-383c0a8e8572","html_url":"https://github.com/Anduin2017/HowToCook","commit_stats":{"total_commits":1579,"total_committers":360,"mean_commits":4.386111111111111,"dds":0.8606713109563014,"last_synced_commit":"38ef07de7d1766d1dc8e4cbf3ab0b09ac099ce27"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/Anduin2017/HowToCook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anduin2017%2FHowToCook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anduin2017%2FHowToCook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anduin2017%2FHowToCook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anduin2017%2FHowToCook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Anduin2017","download_url":"https://codeload.github.com/Anduin2017/HowToCook/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Anduin2017%2FHowToCook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29499802,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T03:57:51.541Z","status":"ssl_error","status_checked_at":"2026-02-16T03:55:59.854Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chinese","cookbook","cooking","dishes","recipes"],"created_at":"2024-09-24T20:33:27.669Z","updated_at":"2026-02-16T04:01:09.261Z","avatar_url":"https://github.com/Anduin2017.png","language":"Dockerfile","funding_links":[],"categories":[],"sub_categories":[],"readme":"const { readdir, writeFile, stat } = require('fs/promises');\nconst fs = require('fs').promises;\nconst path = require('path');\n\nconst README_PATH = './README.md';\nconst MKDOCS_PATH = 'mkdocs.yml';\nconst dishesFolder = 'dishes';\nconst starsystemFolder = 'starsystem';\n\nconst ignorePaths = ['.git', 'README.md', 'node_modules', 'CONTRIBUTING.md', '.github'];\n\nconst categories = {\n  vegetable_dish: {\n    title: '素菜',\n    readme: '',\n    mkdocs: '',\n  },\n  meat_dish: {\n    title: '荤菜',\n    readme: '',\n    mkdocs: '',\n  },\n  aquatic: {\n    title: '水产',\n    readme: '',\n    mkdocs: '',\n  },\n  breakfast: {\n    title: '早餐',\n    readme: '',\n    mkdocs: '',\n  },\n  staple: {\n    title: '主食',\n    readme: '',\n    mkdocs: '',\n  },\n  'semi-finished': {\n    title: '半成品加工',\n    readme: '',\n    mkdocs: '',\n  },\n  soup: {\n    title: '汤与粥',\n    readme: '',\n    mkdocs: '',\n  },\n  drink: {\n    title: '饮料',\n    readme: '',\n    mkdocs: '',\n  },\n  condiment: {\n    title: '酱料和其它材料',\n    readme: '',\n    mkdocs: '',\n  },\n  dessert: {\n    title: '甜品',\n    readme: '',\n    mkdocs: '',\n  },\n};\n\nasync function countStars(filename) {\n  const data = await fs.readFile(filename, 'utf-8');\n  let stars = 0;\n  const lines = data.split('\\n');\n  lines.forEach(line =\u003e {\n    stars += (line.match(/★/g) || []).length;\n  });\n  return stars;\n}\n\nasync function organizeByStars(dishesFolder, starsystemFolder) {\n  const dishes = {};\n\n  async function processFolder(folderPath) {\n    const files = await readdir(folderPath);\n    for (const filename of files) {\n      const filepath = path.join(folderPath, filename);\n      const fileStat = await stat(filepath);\n      if (fileStat.isFile() \u0026\u0026 filename.endsWith('.md')) {\n        const stars = await countStars(filepath);\n        dishes[filepath] = stars;\n      } else if (fileStat.isDirectory()) {\n        await processFolder(filepath);\n      }\n    }\n  }\n\n  const dishesFolderAbs = path.resolve(dishesFolder);\n  const starsystemFolderAbs = path.resolve(starsystemFolder);\n\n  if (!await fs.access(starsystemFolderAbs).then(() =\u003e true).catch(() =\u003e false)) {\n    await fs.mkdir(starsystemFolderAbs, { recursive: true });\n  }\n\n  if (!await fs.access(dishesFolderAbs).then(() =\u003e true).catch(() =\u003e false)) {\n    console.log(`Directory not found: ${dishesFolderAbs}, creating directory...`);\n    await fs.mkdir(dishesFolderAbs, { recursive: true });\n  }\n\n  await processFolder(dishesFolderAbs);\n\n  const starRatings = Array.from(new Set(Object.values(dishes))).sort((a, b) =\u003e a - b);\n  const navigationLinks = []; \n\n  for (const stars of starRatings) {\n    const starsFile = path.join(starsystemFolderAbs, `${stars}Star.md`);\n    const content = [`# ${stars} 星难度菜品`, ''];\n    for (const [filepath, starCount] of Object.entries(dishes)) {\n      if (starCount === stars) {\n        const relativePath = path.relative(starsystemFolderAbs, filepath).replace(/\\\\/g, '/');\n        content.push(`* [${path.basename(filepath, '.md')}](./${relativePath})`);\n      }\n    }\n    await writeFile(starsFile, content.join('\\n'), 'utf-8');\n    navigationLinks.push(`- [${stars} 星难度](${path.relative(path.dirname(README_PATH), starsFile).replace(/\\\\/g, '/')})`);\n    }\n\n  return navigationLinks;\n}\n\nasync function main() {\n  try {\n    let README_BEFORE = '', README_MAIN = '', README_AFTER = '';\n    let MKDOCS_BEFORE = '', MKDOCS_MAIN = '', MKDOCS_AFTER = '';\n    const markdownObj = await getAllMarkdown('.');\n    \n    // Debug logging to understand the structure of markdownObj\n    console.log(\"Markdown Object Structure:\", JSON.stringify(markdownObj, null, 2));\n    \n    for (const markdown of markdownObj) {\n      console.log(\"Processing markdown:\", markdown);\n      if (markdown.path.includes('tips/advanced')) {\n        README_AFTER += inlineReadmeTemplate(markdown.file, markdown.path);\n        MKDOCS_AFTER += inlineMkdocsTemplate(markdown.file, markdown.path);\n        continue;\n      }\n\n      if (markdown.path.includes('tips')) {\n        README_BEFORE += inlineReadmeTemplate(markdown.file, markdown.path);\n        MKDOCS_BEFORE += inlineMkdocsTemplate(markdown.file, markdown.path);\n        continue;\n      }\n\n      for (const category of Object.keys(categories)) {\n        if (!markdown.path.includes(category)) continue;\n        categories[category].readme += inlineReadmeTemplate(markdown.file, markdown.path);\n        categories[category].mkdocs += inlineMkdocsTemplate(\n          markdown.file,\n          markdown.path,\n          true,\n        );\n      }\n    }\n\n    for (const category of Object.values(categories)) {\n      README_MAIN += categoryReadmeTemplate(category.title, category.readme);\n      MKDOCS_MAIN += categoryMkdocsTemplate(category.title, category.mkdocs);\n    }\n\n    let MKDOCS_TEMPLATE;\n    let README_TEMPLATE;\n\n    try {\n      MKDOCS_TEMPLATE = await fs.readFile(\"./.github/templates/mkdocs_template.yml\", \"utf-8\");\n    } catch (error) {\n      MKDOCS_TEMPLATE = `site_name: My Docs\\nnav:\\n  {{main}}\\n`;\n      console.warn(\"mkdocs_template.yml not found, using default template\");\n    }\n\n    try {\n      README_TEMPLATE = await fs.readFile(\"./.github/templates/readme_template.md\", \"utf-8\");\n    } catch (error) {\n      README_TEMPLATE = `# My Project\\n\\n{{before}}\\n\\n{{main}}\\n\\n{{after}}`;\n      console.warn(\"readme_template.md not found, using default template\");\n    }\n\n    const navigationLinks = await organizeByStars(dishesFolder, starsystemFolder);\n    // Debug logging to ensure navigationLinks is defined and contains data\n    console.log(\"难度索引\", navigationLinks);\n    const navigationSection = `\\n### 按难度索引\\n\\n${navigationLinks.join('\\n')}`;\n\n    await writeFile(\n      README_PATH,\n      README_TEMPLATE\n        .replace('{{before}}', README_BEFORE.trim())\n        .replace('{{index_stars}}', navigationSection.trim())\n        .replace('{{main}}', README_MAIN.trim())\n        .replace('{{after}}', README_AFTER.trim()),\n    );\n\n    await writeFile(\n      MKDOCS_PATH,\n      MKDOCS_TEMPLATE\n        .replace('{{before}}', MKDOCS_BEFORE)\n        .replace('{{main}}', MKDOCS_MAIN)\n        .replace('{{after}}', MKDOCS_AFTER),\n    );\n\n    // Organize files by star rating\n    //await organizeByStars(dishesFolder, starsystemFolder);\n  } catch (error) {\n    console.error(error);\n  }\n}\n\nasync function getAllMarkdown(dir) {\n  const paths = [];\n  const files = await readdir(dir);\n  files.sort((a, b) =\u003e a.localeCompare(b, 'zh-CN'));\n\n  for (const file of files) {\n    const filePath = path.join(dir, file);\n    if (ignorePaths.includes(file)) continue;\n    const fileStat = await stat(filePath);\n    if (fileStat.isFile() \u0026\u0026 file.endsWith('.md')) {\n      paths.push({ path: dir, file });\n    } else if (fileStat.isDirectory()) {\n      const subFiles = await getAllMarkdown(filePath);\n      paths.push(...subFiles);\n    }\n  }\n  return paths;\n}\n\nfunction inlineReadmeTemplate(file, path) {\n  return `- [${file.replace('.md', '')}](${path}/${file})\\n`;\n}\n\nfunction categoryReadmeTemplate(title, inlineStr) {\n  return `\\n### ${title}\\n\\n${inlineStr}`;\n}\n\nfunction inlineMkdocsTemplate(file, path, isDish = false) {\n  return `${' '.repeat(isDish ? 10 : 6)}- ${file.replace('.md', '')}: ${path}/${file}\\n`;\n}\n\nfunction categoryMkdocsTemplate(title, inlineStr) {\n  return `\\n${' '.repeat(6)}- ${title}:\\n${inlineStr}`;\n}\n\nmain();\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanduin2017%2Fhowtocook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanduin2017%2Fhowtocook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanduin2017%2Fhowtocook/lists"}