项目说明
这是一个可直接运行的 Deno CLI,演示命令解析、文件持久化和基本任务状态管理。
运行方法
安装 Deno 后执行 deno task test;使用 deno run -A main.ts add "Write docs" 添加任务,list 查看,done <id> 完成。数据保存在当前目录的 JSON 文件中。
验证方式
测试覆盖添加、列出、完成和无效 id,断言输出与文件内容。注意并发写入可能覆盖数据,生产使用应加锁或改用数据库。CLI 应返回明确退出码,并在文件损坏时给出可恢复的错误而不是静默重置。
验收清单
完成「任务跟踪 CLI — 可运行 Deno 项目」后,按节点或操作步骤逐项自查:准备可复现的本地环境,执行文档中的命令,记录输出与失败信息,并确认权限、空数据和失败恢复路径都有对应证据。
代码示例
# No install needed for Deno — just allow read/write permissions
deno run --allow-read=tasks.json --allow-write=tasks.json cli.ts add "Buy groceries"
deno run --allow-read=tasks.json --allow-write=tasks.json cli.ts list
deno run --allow-read=tasks.json --allow-write=tasks.json cli.ts complete 1
deno run --allow-read=tasks.json --allow-write=tasks.json cli.ts delete 1
task-tracker/
├── cli.ts # Entry point: parse args and dispatch commands
├── store.ts # JSON file persistence layer
├── types.ts # Task type definition
├── tasks.json # Auto-created data file
└── store.test.ts # Unit tests for store operations
// cli.ts
import { loadTasks, saveTasks, addTask, completeTask, deleteTask, listTasks } from './store.ts';
const [command, ...args] = Deno.args;
try {
switch (command) {
case 'add': {
const title = args.join(' ');
if (!title) throw new Error('Usage: add <task title>');
const tasks = loadTasks();
const newTask = addTask(tasks, title);
saveTasks(tasks);
console.log(`Task #${newTask.id} added: "${newTask.title}"`);
break;
}
case 'list': {
const tasks = loadTasks();
listTasks(tasks).forEach(t => {
const status = t.done ? '✓' : '○';
console.log(`${status} #${t.id} ${t.title}`);
});
break;
}
case 'complete': {
const id = Number(args[0]);
const tasks = loadTasks();
completeTask(tasks, id);
saveTasks(tasks);
console.log(`Task #${id} marked done`);
break;
}
case 'delete': {
const id = Number(args[0]);
const tasks = loadTasks();
deleteTask(tasks, id);
saveTasks(tasks);
console.log(`Task #${id} deleted`);
break;
}
default:
console.log('Usage: cli.ts <add|list|complete|delete> [args]');
}
} catch (err) {
console.error(`Error: ${err.message}`);
Deno.exit(1);
}
// store.ts
export interface Task {
id: number;
title: string;
done: boolean;
createdAt: string;
}
export function loadTasks(): Task[] {
try {
const raw = Deno.readTextFileSync('tasks.json');
return JSON.parse(raw);
} catch {
return []; // File doesn't exist yet → start fresh
}
}
export function saveTasks(tasks: Task[]): void {
Deno.writeTextFileSync('tasks.json', JSON.stringify(tasks, null, 2));
}
export function addTask(tasks: Task[], title: string): Task {
const task: Task = {
id: tasks.length > 0 ? Math.max(...tasks.map(t => t.id)) + 1 : 1,
title,
done: false,
createdAt: new Date().toISOString(),
};
tasks.push(task);
return task;
}
export function completeTask(tasks: Task[], id: number): void {
const task = tasks.find(t => t.id === id);
if (!task) throw new Error(`Task #${id} not found`);
task.done = true;
}
export function deleteTask(tasks: Task[], id: number): void {
const idx = tasks.findIndex(t => t.id === id);
if (idx === -1) throw new Error(`Task #${id} not found`);
tasks.splice(idx, 1);
}
export function listTasks(tasks: Task[]): Task[] {
return tasks;
}
deno test
Check file:///.../store.test.ts
ok | 4 passed (4) | 0 failed (0)
Duration 1.23s