五、inquirer
inquirer 是一个用户与命令行交互工具。github.com/SBoudrias/I…
1. 安装依赖
yarn add inquirer
当展示出所有的任务后,实际上需要上下移动光标,然后执行后续的操作,因此就要借助 inquirer 库来实现这个目标!
2. 操作任务(功能 4)
// index.js const db = require('./db'); const inquirer = require('inquirer'); // 添加新任务 module.exports.add = async (taskContent) => { // 1.读取文件 const list = await db.read(); // 2.添加一个任务 list.push({title: taskContent, completed: false}); // 3.将任务写入文件 await db.write(list); } // 清空任务列表 module.exports.clear = async () => { await db.write([]); } // 展示所有事项 module.exports.showAll = async () => { // 1. 读出之前的任务 const list = await db.read(); // 2. 打印之前的任务 // list.forEach((task, index) => { // console.log(`${task.completed ? '[x]' : '[_]'} ${index + 1} -> ${task.title}`); // }); // 发起询问 inquirer .prompt({ type: 'list', name: 'index', message: '你想要执行哪一项任务?', choices: [ { name: '+ 添加任务', value: '-2'}, { name: '- 退出', value: '-1'}, ...list.map((task, index) => { return { name: `${task.completed ? '[x]' : '[_]'} ${index + 1} -> ${task.title}`, value: index } }) ], }) .then((answer) => { const index = parseInt(answer.index); if (index >= 0) { // 选中了一个任务 inquirer.prompt({ type: 'list', name: 'action', message: '请选择操作', choices: [ {name: '退出', value: 'quit'}, {name: '已完成', value: 'completed'}, {name: '未完成', value: 'incomplete'}, {name: '改标题', value: 'updateTitle'}, {name: '删除', value: 'remove'}, ] }).then(answer => { console.log(answer.action); switch (answer.action) { case 'completed': list[index].completed = true; db.write(list) break; case 'incomplete': list[index].completed = false; db.write(list) break; case 'updateTitle': inquirer.prompt({ type: 'input', name: 'title', message: '请输入新的标题', default: list[index].title // 原标题 }).then(answer => { list[index].title = answer.title; db.write(list); }); break; case 'remove': list.splice(index, 1); db.write(list); break; } }) } else if (index === -2) { // 添加任务 inquirer.prompt({ type: 'input', name: 'title', message: '请添加新任务标题', }).then(answer => { list.push({ title: answer.title, completed: false }) db.write(list); }) } }); }
3. 代码优化
// index.js const db = require('./db'); const inquirer = require('inquirer'); // 1. 添加新任务 module.exports.add = async (taskContent) => { // 1.读取文件 const list = await db.read(); // 2.添加一个任务 list.push({title: taskContent, completed: false}); // 3.将任务写入文件 await db.write(list); } // 2. 清空任务列表 module.exports.clear = async () => { await db.write([]); } // 3.2.2 添加新任务 function askForAddNewTask (list) { inquirer.prompt({ type: 'input', name: 'title', message: '请添加新任务标题', }).then(answer => { list.push({ title: answer.title, completed: false }) db.write(list); console.log('添加成功!'); }) } // 3.2.1.1 设置已完成状态 async function setCompletedState (list, index) { list[index].completed = true; await db.write(list); console.log('当前任务已完成!'); } // 3.2.1.2 设置未完成状态 async function setIncompleteState (list, index) { list[index].completed = false; await db.write(list); console.log('当前任务待完成...'); } // 3.2.1.3 修改标题 function updateTitle (list, index) { inquirer.prompt({ type: 'input', name: 'title', message: '请输入新的标题', default: list[index].title // 原标题 }).then(answer => { list[index].title = answer.title; db.write(list); console.log('标题更新成功!'); }); } // 3.2.1.4 移除任务 async function removeTask (list, index) { list.splice(index, 1); await db.write(list); console.log('删除成功!'); } // 3.2.1 后续操作 function askForNextAction (list, index) { const actions = { setCompletedState, setIncompleteState, updateTitle, removeTask } inquirer.prompt({ type: 'list', name: 'action', message: '请选择操作', choices: [ {name: '退出', value: 'quit'}, {name: '已完成', value: 'setCompletedState'}, {name: '未完成', value: 'setIncompleteState'}, {name: '改标题', value: 'updateTitle'}, {name: '删除', value: 'removeTask'}, ] }).then(answer => { const currentAction = actions[answer.action]; currentAction && currentAction(list, index); // switch (answer.action) { // case 'setCompletedState': // setCompletedState(list, index); // break; // case 'setIncompleteState': // setIncompleteState(list, index); // break; // case 'updateTitle': // updateTitle(list, index); // break; // case 'removeTask': // removeTask(list, index); // break; // } }) } // 3.2 打印之前的任务 + 后续操作 function displayTasks (list) { inquirer .prompt({ type: 'list', name: 'index', message: '你想要执行哪一项任务?', choices: [ { name: '+ 添加任务', value: '-2'}, { name: '- 退出', value: '-1'}, ...list.map((task, index) => { return { name: `${task.completed ? '[x]' : '[_]'} ${index + 1} -> ${task.title}`, value: index } }) ], }) .then((answer) => { const index = parseInt(answer.index); if (index >= 0) { // 3.2.1 选中了一个任务,执行后续操作 askForNextAction(list, index); } else if (index === -2) { // 3.2.2 添加新任务 askForAddNewTask(list); } }); } // 3. 展示所有事项 module.exports.showAll = async () => { // 3.1 读出之前的任务 const list = await db.read(); // 3.2 打印之前的任务 displayTasks(list); }
六、代码发布
1. 设置 shebang
让用户自动执行 node,参考:zhuanlan.zhihu.com/p/262456371
在 cli.js 中的首行添加一段 shebang 代码:
// cli.js #!/usr/bin/env node
2. 配置 package.json
{ "name": "cpc-node-todo-1", "bin": { "cpc-todo": "./cli.js" }, "files": [ "cli.js", "db.js", "index.js" ], "version": "0.0.3", "main": "index.js", "license": "MIT", "dependencies": { "commander": "^8.0.0", "inquirer": "^8.1.2" } }
- name 字段表示,包的名称,以后通过这个名称来下载使用。
- bin 字段表示,以后用户在终端中可直接运行的命令。
- files 字段表示,需要打包上传的文件。这里是 cli.js、index.js、db.js 三个文件,当然如果说,你的目录里就只有这三个 js 文件的话,那么该字段就可以直接写成: "files": ["*.js"]
3. 发布到 NPM
- 1.npm login (此步骤需要填写用户名和密码,以及邮箱。)
- 2.npm publish
- 3.npm logout
4. 下载使用
- 1.打开终端,全局安装:
npm i -g cpc-node-todo-1
- 2.安装好后,通过 cpc-todo 来调用这个 node 应用,注意这个 cpc-todo 就是打包之前 package.json 中设置的 bin 字段的内容。
- 3.如果不想用了,通过以下命令来卸载即可: npm un -g cpc-todo-1
5. 包的更新
略。
七、单元测试
1. jest
www.jestjs.cn/docs/gettin…
npm install --save-dev jest