# 85. 文件模块

文件模块 fs Node.js 全解

本节课目标: 创建一个基于文件的todo工具

## 需求

**功能**

可以列出所有的 todo 可以新增 todo 可以编辑 todo 可以删除todo 可以标记todo为已完成/未完成

**命令**

```js
t
t add 任务名
t clear
```

**CRM 学习法**

Copy 抄

Run运行

Modify改

这个项目会用到下面几个库

```
"commander"
"inquirer"
"winston"

devDependencies
"jest"
```

## 初始化项目

```
yarn init -y
```

`init -y`会创建一个`package.json`的文件

```
{
  "name": "node-todo-2",
  "version": "0.0.1",
  "main": "index.js",
  "license": "MIT"
}
```

这里把版本号改成0.0.1

创建一个index.js的文件，里面输入

```
console.log("Hi, I am Node.js")
```

如何运行这行代码呢？

在node.js中，只需要运行`node index`即可。

### 安装commander.js

<https://github.com/tj/commander.js>

```
yarn add commander  
```

把index.js的内容替换成

`.option`

```javascript
const program = require("commander");
program
  .option("-d, --debug", "output extra debugging")
  .option("-s, --small", "small pizza size")
  .option("-p,  --pizza-type <type>", "flavour of pizza");
program.parse(process.argv);

console.log("pizza details:");//敲这一行为了确定indexjs是不是在运行

```

运行`node index`

运行`node index -h`, 就会看到下面的显示。

```bash
Usage: index [options]

Options:
  -d, --debug               output extra debugging
  -s, --small               small pizza size
  -p,  --pizza-type <type>  flavour of pizza
  -h, --help                display help for command
```

因此，.option就是用来声明有哪些选项的。`-h`是默认的。

`.command`

```javascript
program
  .command("add <taskName>")
  .description("add a task")
  .action(() => {
    console.log("hi");
  });
```

以下是输出

```bash
Usage: index [options] [command]

Options:
  -x, --xxx,      output extra debugging
  -h, --help      display help for command

Commands:
  add <taskName>  add a task
  help [command]  display help for command
```

`add`

```javascript
program
  .command("add")
  .description("add a task")
  .action((...args) => {
    const words = args.slice(0, -1).join(" ");
    console.log(words);
  });
```

`a+`

Open file for reading and appending. The file is created if it does not exist.

命令行

查看.todo文件里有什么

```bash
cat ~/.todo
```

删除文件

```bash
rm ~/.todo
```

优化代码

```javascript
//***index.js

//获取home目录
const home = process.env.HOME || homedir;
const p = require("path");
const dbPath = p.join(home, ".todo");
const fs = require("fs");

module.exports.add = (title, done) => {
  //读取之前的任务
  fs.readFile(dbPath, { flag: "a+" }, (error, data) => {
    if (error) {
      console.log(error);
    } else {
      let list;
      try {
        list = JSON.parse(data.toString());
      } catch (error2) {
        list = [];
      }

      const task = {
        title: title,
        done: false,
      };
      list.push(task);
      const string = JSON.stringify(list);
      fs.writeFile(dbPath, string + "\n", (error3) => {
        if (error3) {
          console.log(error3);
        }
      });
    }
  });
};

```

```javascript
console.log(process.argv);
```

`node cli`会打印出

```bash
'/opt/homebrew/Cellar/node/19.6.0/bin/node',//代表第一个参数node
'/Users/clair/github/jirengu-class/node-todo-2/cli'//代表第二个参数cli
```

安装`inquirer`

```
yarn add inquirer
```

<https://devdocs.io/node/fs#fsreadfilepath-options-callback>

[https://devdocs.io/node/fs#file-system-flags](https://devdocs.io/node/fs#fsreadfilepath-options-callback)

<https://devdocs.io/node/fs#fswritefilefile-data-options-callback>

**这节课很多优化代码的技巧可以学一下**

**技巧：把一堆代码取一个名字，然后单独拆分成函数写**

项目代码没敲完，特别是最后优化那一部分。

## 作业：

请按照视频制作一个 node-todo 项目，把源代码上传到 GitHub，并给出运行成功的截图\~

## 回顾

如何使用 return new Promise

如何使用 async await

什么时候 reject(error)什么时候不 reject(error)

如何使用 fs.readFile/ fs.writeFile

如何使用 logger

如何使用 commander

如何使用 module.exports

如何引入jest 测试

如何 mock fs

如何使用WebStorm 调试

如何使用--inspect-brk 调试

## 发布到 npm

修改 package.json

```javascript
"bin":{  "t":",/cli.js"
}，
"files": [  "*.js"  ]，
```

运行命令

```bash
yarn login 和 yarn publish
npm adduser
npm publish
```

注意要把淘宝源切换为原始源

如果提示出错，请自己把出错理解一下

## npm publish

**常见错误**

你发布的包名跟线上包名重复，不允许发布，请改名

你用户名密码不匹配，请重置密码

你没有改版本号，不能重复发布同一版，请升级版本

其他问题自行Google

## 总结

**不要看书学习**

看了忘 没有留下任何痕迹 做笔记稍微好点)

**动手学习**

照着我的代码敲 (不允许复制粘贴)

学习调试方法

发现bug，学会自己debug

持续改进自己的作品 即使课堂上没讲)

下节课学习另一个模块

再见
