时间: 2022.第22周 晚 20:00
需求背景
我模仿大佬的学习记录 Today I Learned,自建了一个仓库,希望将我日常学到的小知识点记录下来,并按一定的规则进行分类。
暂时想到的类别就是 编程语言 、 技术栈 等,每个类别拥有一个自己的文件夹,然后可以根据文件夹内的文件名,生成对应的markdown链接,并汇总到README.MD中,就像是一个目录。
任务点拆分
- 扫描项目目录,根据文件夹名称生成类别名
- 生成类别名后,写入README.MD,生成二级菜单文本内容
- 扫描类别文件夹下的所有文件,根据文件名称生成文章名
- 生成文章名后,写入README.MD,生成三级菜单文本内容
- 将上述功能点做成
npm script,甚至是git hook,每次commit完毕后,自动更新README.MD
单元测试
由于涉及到文件夹、文件,所以或许需要mock ?
技术点
- 遍历目标下的所有文件夹(无需递归至子文件夹)
- 遍历目标文件夹下所有的md文件
- 写入(完全覆盖)文件内容
实现过程
好吧,我们先想办法获取项目根目录的所有文件夹,因为我想将categories文件夹直接平铺开(因为大佬就是这么做的哈哈哈哈),这样显地我学了那么那么多种类的东西~
理所当然写出了如下代码
const result = await fs.readdir("./");
readdir,怎么也应该是读取文件夹嘛,我这样猜想,但是输出结果打脸了:
result
(12) ['.git', '.gitignore', '.pnpm-debug.log', '.vscode', 'genREADMEcontent.ts', 'js', 'LICENSE', 'node_modules', 'package.json', 'pnpm-lock.yaml', 'README.md', 'tsconfig.json']
读读文档,修改如下:
async function readCategoriesFolder() {
const result = await fs.readdir("./", {
encoding: "utf-8",
withFileTypes: true,
});
const directories = result.filter((file) => {
return file.isDirectory();
});
}
// 输出
['.git', '.vscode', 'js', 'node_modules']
这次对了。但是如.git等文件夹似乎只能写死过滤规则了
export function filterUselessDirectories(dir: Dirent) {
const target = ["node_modules"];
const reg = /^\..+/; // 过滤掉以 "." 开头的文件夹
return !target.includes(dir.name) && !reg.test(dir.name);
}
后面的事情就容易了。按照想要的排版内容生成string即可。 实现的方案可以是自己拼接markdown字符串,也可以是类似mjs的形式,后者没用过,回头有机会可以试试。
这里要注意,forEach中await也是没用的,依旧会往下继续执行,所以使用for of替代
husky实现自动更新README
先在 package.json里增加我们的 script
"updateDoc": "node ./gen.js"
然后在pre-commit的git hook里实现:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
#npm test
npm run updateDoc
git add README.md
优化点
- 蠢了,其实用
path实现会更优雅。自己操作file好麻烦哩 重写后:
const path = require("path");
const glob = require("glob");
const fs = require("node:fs/promises");
async function pathBootstrap() {
const dirs = glob.sync("*/").filter(filterUselessDirectories);
// 生成文件夹目录
let str = dirs.reduce((pre, cur) => {
const dirName = path.basename(cur);
pre += `* [${dirName}](#${dirName}) \n`;
return pre;
}, "## Categories\n\n");
str += "\n---\n\n";
dirs.forEach((dir) => {
const dirName = path.basename(dir);
str += `### ${dirName}\n\n`;
const files = glob.sync(dir + "*.md");
files.forEach((file) => {
const extension = path.extname(file); // 获取后缀名
const fileName = path.basename(file, extension); // 获取没有后缀的文件名
str += `* [${fileName}](${file}) \n`;
});
});
await writeREADME(str);
}
function filterUselessDirectories(dirName) {
const target = ["node_modules/"];
const reg = /^\..+/; // 过滤掉以 "." 开头的文件夹
return !target.includes(dirName) && !reg.test(dirName);
}
async function writeREADME(content) {
await fs.writeFile("README.md", content, {
encoding: "utf-8",
});
}
pathBootstrap();
- 自己折腾这些其实意义不大。因为都有现成的包啦...