-->
ads here

Build your CLI with NodeJS

advertise here
Xin chào các bạn, trong bài viết hôm nay, mình sẽ hướng dẫn các bạn tạo 1 CLI đơn giản bằng NodeJS.
enter image description here

Table of content

  1. Giới thiệu
  2. What to build
  3. Dependencies
  4. Coding
  5. Publish to NPM

1. Giới thiệu

Là developer, chắc hắn các bạn đã từng làm việc CLI (Command Line Interface), từ git đến mongdb, heroku …, có thể thấy chúng ta chạm mặt với CLI rất nhiều trong quá trình làm việc của mình. Vậy tại sao chúng ta không thử tạo một CLI cho riêng mình nhỉ?

2. What to build

Bài viết này sẽ hướng dẫn các bạn build 1 Todo-cli đơn giản và publish nó lên NPM.
Todo-cli của chúng ta sẽ gồm các lệnh sau
  • add <task>: tạo một task mới
  • done <id>: đánh dấu 1 task đã hoàn thành
  • show: hiển thị danh sách các task hiện tại
  • delete <id>: xoá 1 task

3. Dependencies

Để làm được Todo-cli chúng ta cần một số tool và package sau
  • chalk: Styling string trong terminal
  • clear: Xoá màn hình của terminal
  • cli-table: Tạo giao diện bảng trong terminal
  • commander: tạo command-line interface bằng Nodejs
  • figlet: tạo FIGfont trong Javascript
  • knex: SQL query buider cho Nodejs
  • sqlite3: Asynchronous, non-blocking SQLite3 bindings for Node.js

4.Coding

Sau khi chuẩn bị xong chúng ta sẽ bắt đầu tiến hành code.
4.1. Khởi tạo project
Tạo 1 thư mục tên todo-cli, sau đó init NodeJS project bằng lệnh
  $ npm init -y
Sau đó ta sửa file package.json như sau:
{ "name": "julian-todo-cli", "version": "1.0.0", "main": "./src/index.js", "license": "MIT", "scripts": { "start": "nodemon --watch 'src/index.js'", "setup": "npm i -g" }, "bin": { "todo": "./src/index.js" }, "dependencies": { "chalk": "^2.4.2", "clear": "^0.1.0", "cli-table": "^0.3.1", "commander": "^2.20.0", "figlet": "^1.2.1", "knex": "^0.17.0", "sqlite3": "^4.0.8" } }
4.2. Khởi tạo Database
Ở đây ta sẽ sử dụng database để lưu các task đã được tạo, cũng như update và xoá task tương ứng. Chúng ta sẽ sử dụng knexsqlite3 để hiện thực database
Tạo thư mục database, trong đó tạo file index.js có nội dung như sau

const knex = require('knex')({ client: 'sqlite3', connection: { filename: "./database/data.sqlite3" }, useNullAsDefault: true }) knex.schema .createTable('todos', todo => { todo.increments('id').primary(); todo.string('text'); todo.dateTime('createdAt'); todo.dateTime('updatedAt'); todo.boolean('completed'); }) .then(() => console.log('Todos Table created')) .catch(() => {}); module.exports = knex;

4.3. Tạo các command
Bước tiếp theo chúng ta tiến hành bước quan trọng nhất, là hiện thực CLI. Tạo tạo file src/index.js như sau

#!/usr/bin/env node const chalk = require('chalk'); const clear = require('clear'); const figlet = require('figlet'); const program = require('commander'); clear(); console.log( chalk.green( figlet.textSync('todo - cli',{ font: 'basic', horizontalLayout: 'full' }) ) ); program .version('0.0.1') .description("An example CLI for Todos list") program.parse(process.argv)

Note: chú ý tới dòng đầu tiên #!/usr/bin/env node, dòng này có tác dụng báo cho Shell biết cách thực thi của ứng dụng của chúng ta
Tiến hành chạy thử
 $ node src/index.js
và bạn sẽ thấy kết quả như sau

d888888b .d88b. d8888b. .d88b. .o88b. db d888888b `~~88~~' .8P Y8. 88 `8D .8P Y8. d8P Y8 88 `88' 88 88 88 88 88 88 88 8P 88 88 88 88 88 88 88 88 88 C8888D 8b 88 88 88 `8b d8' 88 .8D `8b d8' Y8b d8 88booo. .88. YP `Y88P' Y8888D' `Y88P' `Y88P' Y88888P Y888888P

Sau khi khởi tạo command xong, chúng ta bắt đầu import database vào và tiến hành viết câu lệnh đầu tiên - thêm 1 task mới
Cập nhật file src/index.js như sau

// ... const database = require('../database'); // ... program .command('add <task>') .description('Add new todo') .action(async task=> { try { await database('todos').insert({ text: task, completed: false, createdAt: Date.now(), updatedAt: Date.now(), }); console.log(`TODO ${chalk.green(task)} created`) process.exit(0) } catch (error) { console.log( chalk.red(error) ) process.exit(1) } })

Lưu ý nhớ thêm dòng process.exit(code) để kết thúc process sau mỗi lần thực hiện 1 lệnh.
Ta test bằng cách
 $ node src/index.js add "Test first todo"
Ta có kết quả như sau
TODO Test first todo created
Tiếp theo ta sẽ hiện thực lệnh show để hiển thị danh sách các task hiện có. Cập nhật src/index.js như sau

//... const Table = require('cli-table'); // Use to render table in terminal // ... program .command('show') .description('List all current todo') .action(async () => { const table = new Table({ head: [ chalk.blueBright('id'), chalk.blueBright('todo'), chalk.blueBright('completed') ] }) try { const todos = await database("todos").select(); if (todos.length === 0) { console.log(chalk.bold.keyword('orange')('You have no todos')) } else { todos.forEach(todo => { const {id, text, completed} = todo; const status = completed !== 0 ? chalk.green('done [✓]') : chalk.red('not completed [✕]'); table.push([id, text, status]) }) console.log(table.toString()) } process.exit(0) } catch(error) { console.log( chalk.red(error) ) process.exit(1) } }) // ...

Chạy thử bằng
 $ node src/index.js show
và đc kết quả như sau:

┌────┬─────────────┬───────────────────┐ │ id │ todo │ completed │ ├────┼─────────────┼───────────────────┤ │ 2 │ First task │ done [] │ ├────┼─────────────┼───────────────────┤ │ 3 │ second task │ not completed [] │ └────┴─────────────┴───────────────────┘

Tiếp theo ta hiện thực 2 command còn lại là deletedone. Khi đó ta có file src/index.js đầy đủ như sau

#!/usr/bin/env node const chalk = require('chalk'); const clear = require('clear'); const figlet = require('figlet'); const program = require('commander'); const Table = require('cli-table'); const database = require('../database'); clear(); console.log( chalk.green( figlet.textSync('todo - cli',{ font: 'basic', horizontalLayout: 'full' }) ) ); program .version('0.0.1') .description("An example CLI for Todos list") program .command('add <task>') .description('Add new todo') .action(async task=> { try { await database('todos').insert({ text: task, completed: false, createdAt: Date.now(), updatedAt: Date.now(), }); console.log(`TODO ${chalk.green(task)} created`) process.exit(0) } catch (error) { console.log( chalk.red(error) ) process.exit(1) } }) program .command('show') .description('List all current todo') .action(async () => { const table = new Table({ head: [ chalk.blueBright('id'), chalk.blueBright('todo'), chalk.blueBright('completed') ] }) try { const todos = await database("todos").select(); if (todos.length === 0) { console.log(chalk.bold.keyword('orange')('You have no todos')) } else { todos.forEach(todo => { const {id, text, completed} = todo; const status = completed !== 0 ? chalk.green('done [✓]') : chalk.red('not completed [✕]'); table.push([id, text, status]) }) console.log(table.toString()) } process.exit(0) } catch(error) { console.log( chalk.red(error) ) process.exit(1) } }) program .command('delete <id>') .description('Delete todo by Id') .action(async id => { try { const [todo] = await database('todos').where("id", id); if (todo) { await database('todos').where("id", id).delete(); console.log(chalk.yellow(`Todo with id ${id} deleted`)) process.exit(0) } else { throw new Error('Todo not found') } } catch(error) { console.log( chalk.red(error) ) process.exit(1) } }) program .command('done <id>') .description('Mark a todo as done') .action(async id => { try { const [todo] = await database('todos').where("id", id); if (todo) { console.log('todo:', todo) await database('todos') .where('id', id) .update({ completed: true, updatedAt: Date.now() }) console.log('Done this todo') process.exit(0) } else { throw new Error('Todo not found') } } catch(error) { console.log( chalk.red(error) ) process.exit(1) } }) program.parse(process.argv)

4.4. Cài đặt cli ở local machine
Để cài và sử dụng todo-cli vừa tạo, chúng ta cần cài đặt ở global.
Chạy lệnh sau:
 $ npm run setup
Và được kết quả:

> todo-cli@1.0.0 setup /Users/juliandong/Desktop/todo-cli > npm i -g /Users/juliandong/.nvm/versions/node/v10.15.3/bin/todo -> /Users/juliandong/.nvm/versions/node/v10.15.3/lib/node_modules/todo-cli/src/index.js + todo-cli@1.0.0 added 1 package in 1.037s

Ta chạy thử bằng lệnh
 $ todo show
và có kết quả

d888888b .d88b. d8888b. .d88b. .o88b. db d888888b `~~88~~' .8P Y8. 88 `8D .8P Y8. d8P Y8 88 `88' 88 88 88 88 88 88 88 8P 88 88 88 88 88 88 88 88 88 C8888D 8b 88 88 88 `8b d8' 88 .8D `8b d8' Y8b d8 88booo. .88. YP `Y88P' Y8888D' `Y88P' `Y88P' Y88888P Y888888P ┌────┬─────────────┬───────────────────┐ │ id │ todo │ completed │ ├────┼─────────────┼───────────────────┤ │ 2 │ First task │ done [✓] │ ├────┼─────────────┼───────────────────┤ │ 3 │ second task │ not completed [✕] │ └────┴─────────────┴───────────────────┘

5. Publish to NPM

Sau khi đã hoàn thành xong việc code, giờ mình sẽ publish cli này lên NPM để các bạn có thể cài và sử dụng thông qua npm install
  1. Đầu tiên, bạn cần có tài khoản NPM ( nếu bạn đã có tài khoản rồi thì có thể bỏ qua bước này
    Tạo tài khoản npm tại npmjs.com.
  2. Sau khi tạo tài khoản xong, bạn mở terminal, gõ lệnh sau để đăng nhập vào npm
 $ npm login
Nhập thông tin cần thiết

  1. Tiến hành publish package
 $ npm publish
Và đây là kết quả

Sau đó bạn vào link này để sẽ thấy package mình vừa publish

Kết

Mình vừa hướng dẫn các bạn cách tạo 1 CLI cho riêng mình bằng NodeJS đồng thời publish lên NPM để mọi người có thể sử dụng. Mình hi vọng qua bài viết này các bạn có thêm được một chút kiến thức để có thể tự tạo và publish CLI cho riêng mình và có thể áp dụng được vào công việc của các bạn.
Mình có để link github để các bạn có thể xem source code kĩ hơn.
Nếu các bạn thấy hay có thể chia sẻ bài viết này cho mọi người cùng biết, nếu có bất kì ý kiến đóng góp xây dựng, đừng ngần ngại để lại comment bên dưới cho mình nhé, xin cảm ơn!

Reference

Advertisement
COMMENTS ()