Build Command-line Interface (CLI) tool với Nodejs

Build Command-line Interface (CLI) tool với Nodejs

Giới thiệu

CLI (Command-line Interface) là giao diện dòng lệnh, nhận các giá trị mà bạn nhập từ terminal và sau đó thực thi dựa trên giá trị mà bạn nhập

1. Dạo bài

Là một developer, một điều mình chắc chắn rằng là bạn không thể không biết đến CLI, những command thường dùng trong phát triển phần mềm như git, node, npm, docker,... hay là những command được built-in sẵn trong hệ thống như pwd, ls, mkdir, cd, touch,...

CLI thường sẽ khó sử dụng khi bạn là người mới, dùng GUI sẽ trực quan và dễ sử dụng hơn, nhưng khi bạn đã quen tay và gặp những tác vụ cần xử lý nhiều thì sử dụng CLI sẽ hữu ích và nhanh hơn

Và nếu bạn làm việc liên quan đến server linux thì việc sử dụng CLI là bắt buộc, như là xây dựng sẵn các command để khởi chạy một ứng dụng chẳng hạn

Trong bài viết này mình sẽ giới thiệu về một thư viện giúp triển khai CLI trong Nodejs, đó là Commander. Và cũng trong bài viết này mình sẽ hướng dẫn giả lập command mkdir (lệnh này giúp tạo thư mục) bằng nodejs. Lưu ý rằng các khái niệm dưới đây đều áp dụng cho các ngôn ngữ/thư viện tạo CLI chứ không riêng Commander trong Nodejs

2. Khái niệm

2.1. Commander là gì?

Commander đơn giản là một thư viện giúp tạo ra các command bằng Nodejs

Lượt download hàng tuần trên npm là hơn 100 triệu (một con số quá khủng) và hơn 24k ⭐️ trên github

2.2. Những khái niệm cần biết

Thường các CLI sẽ có primary-command và nhiều sub-command như git checkout, git branch, npm init, npm install,... nhưng với các CLI built-in sẵn trong linux, macOS thì thường sẽ chỉ dùng primary-command thôi như là pwd, ls, cd, touch,...

Trong bài viết của mình mình sẽ gọi sub-command là command nhé!

Lấy ví dụ trực quan:

docker run -dp 3000:3000 node

  • docker: primary-command, trong primary-command bao gồm nhiều command
  • run: command, run một trong nhiều command của primary-command như run, pull, push, container, image,...
  • -dp: short options, là 2 short options bao gồm -d-p
  • 3000:3000: option-argument, đối số của option -p
  • node: command-argument, đối số của command run

Options:

Các option trong CLI có 2 cách định nghĩa:

  • Short option: chỉ được đặt với 1 dấu gạch ngang (-p, -d, -i)
  • Long option: đặt option với 2 dấu gạch ngang (--publish, --detach, --interactive)

Có thể viết liền các option như sau -dp 3000:3000, nghĩa là -d sẽ nhận giá là true và -p là 3000:3000

Option-argument:

  • Đối số của option có 2 kiểu giá trị: boolean (-d, giá trị là true) hoặc value (--publish 3000:3000)
  • Giá trị của đối số có thể là optional hoặc required
  • Các đối số có thể là một giá trị hoặc là nhiều giá trị
  • Long optionshort option có cách gán argument khác nhau: viết liền, khoảng cách hoặc dấu bằng (Mình hay sử dụng space)
    • Long short: --publish=3000:3000, --publish 3000:3000
    • Short option: -p3000:3000, -p 3000:3000

Command:

  • Bao gồm primary-command và nhiều command
  • Ví dụ: docker run, docker là primary-command, run là command của docker

Command-argument:

  • Là các giá trị được truyền vào command
  • Argument của các command có thể là optional hoặc required, là một giá trị hoặc nhiều giá trị
  • Ví dụ: docker run node, node sẽ là argument của run

Lưu ý:

  • Khi bạn tạo một command cần xác định: Tên command là gì? Mô tả command đó để làm gì. Arguments của command là gì? Gồm những options gì? Mô tả các options đó. Và arguments của options là gì?

  • Khi command-argument có kí tự đặc biệt như dấu gạch ngang (-) hoặc 2 dấu gạch ngang (--) thì bị hiểu thành là option. Giả sử bạn muốn tạo một folder có tên là --test, command sẽ như này: mkdir --test, lúc này tên folder --test được hiểu là một option, và không có option nào là --test nên sẽ bị báo lỗi, để khắc phục vấn đề này bạn thêm 2 dấu gạch ngang ở trước command-argument (mkdir -- --test)

2.3. Giả lập mkdir command

Trong hướng dẫn này, mình sẽ tạo ra một command là mkdir trong primary-command x-command, bao gồm:

  • Cho tạo một hoặc nhiều thư mục cùng lúc
  • Cho tạo sub-folder bằng option là -p, --parent (trong mkdir, muốn tạo sub-folder nhưng chưa có parent folder thì cần option là -p)
  • Hướng dẫn cài đặt command như một CLI global
  • Hướng dẫn deploy lên npm để public cho mọi người có thể install

Bước 1: Init

  • Tạo thư mục: mkdir x-command && cd x-command
  • Init package.json và install commander: npm init -y && npm i commander
  • Tạo thư mục bin/index.js và mở text editor: mkdir bin && vi bin/index.js

Bước 2: Code

#!/usr/bin/env node

const { Command } = require('commander');
const fs = require('fs');

const program = new Command();

program.name('x-command').description('X-COMMAND').version('1.0.0');
program
  .command('mkdir')
  .description('The mkdir utility creates the directories')
  .argument('<directories...>', 'The name directories')
  .option('-p, --parent', 'Create intermediate directories as required')
  .action(function createDirectories(directories, options) {
    const currentWorkingDir = process.cwd();
    for (const directory of directories) {
      const fullPath = `${currentWorkingDir}/${directory}`;
      fs.mkdirSync(fullPath, { recursive: options.parent ? true : false });
    }
  });

program.parse();

Bước 3: Run

# Create multiple folder
node bin/index.js mkdir test1 test2 test3

# Create multiple sub-folder
node bin/index.js mkdir -p test4/test5 test6/test7

Bước 4: Install as global

Những bước trên đã hướng dẫn bạn tạo một dự án mkdir command, nhưng bạn để ý bước 3, khi chạy mkdir thì bạn sẽ chạy bằng node và trỏ tới file js để thực thi. Vậy thì không giống với các CLI bạn hay sử dụng.

Để CLI có thể sử dụng ở bất cứ thư mục nào mà không cần trỏ tới file js các bạn làm theo các bước sau:

  • Thêm thuộc tính bin vào package.json:
     {
        "name": "x-command",
        "version": "1.0.0",
        "description": "",
        "main": "./bin/index.js",
        "bin": {
          "x-command": "./bin/index.js"
        },
        ...
      }
    
  • Cài đặt thư viện: npm install -g .
  • Sử dụng:

      # Create multiple folder
      x-command mkdir test1 test2 test3
    
      # Create multiple sub-folder
      x-command mkdir -p test4/test5 test6/test7
    

Bước 5: Deploy to npm

Tới bước 4 là bạn đã hoàn thành cách tạo ra một CLI rồi. Nhưng sau đó bạn muốn những người bạn của mình có thể sử dụng CLI mà chính bạn tạo ra thì sao? Bằng cách deploy lên npm thì các bạn của bạn có thể install như những thư viện khác và sử dụng CLI do bạn tạo ra

  • Trước hết thì bạn cần phải tạo một tài khoản npm: Home
  • Sau khi tạo tài khoản thành công, bạn nhập lệnh npm login ở terminal
  • Tiếp theo là npm publish (Lưu ý rằng các bạn cần đặt name trong package.json không trùng với các package khác nhé)

3. Tổng kết

Congratulations 🎉🎉🎉. Như vậy các bạn đã biết cách tạo CLI tool với Nodejs. Để phát triển nhiều hơn trên con đường lập trình thì việc thành thạo CLI là điều không thể thiếu, vì vậy hãy sử dụng CLI nhiều hơn nhé. Các bạn có thể kham khảo resource github tại đây hoặc install package ở đây. Cảm ơn các bạn đã đọc