0

[Series yargs - Bài 2] Xử Lý Dữ Liệu Phức Tạp & Cấu Trúc Code Chuẩn "Clean Code"

Chào các bạn quay trở lại với series yargs! Ở Bài 1, chúng ta đã biết cách tạo ra một công cụ dòng lệnh cơ bản. Tuy nhiên, thực tế đòi hỏi nhiều hơn thế: Người dùng có thể muốn truyền vào một danh sách (mảng), hoặc chúng ta muốn ép họ chỉ được chọn một trong vài giá trị cố định.

Quan trọng hơn, làm sao để cấu trúc project khi CLI của bạn có tới hàng chục lệnh khác nhau? Hôm nay, chúng ta sẽ giải quyết triệt để hai bài toán này.

1. Tham số nâng cao: Arrays (Mảng) và Choices (Ràng buộc chọn)

Giả sử chúng ta đang viết một CLI để... đặt Pizza (order). Khách hàng có thể chọn kích cỡ (bắt buộc phải là S, M, hoặc L) và thêm nhiều loại topping (phô mai, xúc xích, nấm...).

Đây là lúc choices và type: 'array' tỏa sáng:

// file: app.js
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');

yargs(hideBin(process.argv))
  .command({
    command: 'order',
    describe: 'Đặt một chiếc Pizza',
    builder: {
      size: {
        describe: 'Kích cỡ bánh',
        demandOption: true,
        type: 'string',
        choices: ['S', 'M', 'L'] // Ép người dùng chỉ được nhập 1 trong 3 giá trị này
      },
      toppings: {
        describe: 'Các loại topping thêm vào',
        type: 'array', // Nhận vào một mảng các chuỗi
        default: ['cheese'] // Nếu không nhập, mặc định là có phô mai
      }
    },
    handler: (argv) => {
      console.log(`🍕 Đang chuẩn bị Pizza size [${argv.size}]...`);
      console.log(`🧀 Toppings: ${argv.toppings.join(', ')}`);
    }
  })
  .strict()
  .help()
  .parse();

Hãy thử gõ sai:

node app.js order --size XL

yargs sẽ ngay lập tức chặn lại và chửi mắng (rất lịch sự): Invalid values: Argument: size, Given: "XL", Choices: "S", "M", "L"

Cách truyền một mảng (array) vào CLI:

node app.js order --size L --toppings mushroom sausage onion
# Kết quả: 
# 🍕 Đang chuẩn bị Pizza size [L]...
# 🧀 Toppings: mushroom, sausage, onion

2. "Tách file" với Command Modules (Clean Code)

Để tránh việc file app.js dài hàng nghìn dòng, yargs hỗ trợ khái niệm Command Module. Bạn có thể tách mỗi lệnh ra một file riêng biệt.

Bước 1: Tạo một thư mục tên là cmds (hoặc commands). Bước 2: Tạo một file tên là order.js bên trong thư mục đó. File này chỉ cần export ra 4 thuộc tính cơ bản: command, describe, builder, và handler.

// file: cmds/order.js
module.exports = {
  command: 'order',
  describe: 'Đặt một chiếc Pizza',
  builder: (yargs) => {
    return yargs
      .option('size', {
        describe: 'Kích cỡ bánh',
        demandOption: true,
        choices: ['S', 'M', 'L']
      })
      .option('toppings', {
        type: 'array',
        default: ['cheese']
      });
  },
  handler: (argv) => {
    console.log(`🍕 Đang chuẩn bị Pizza size [${argv.size}]...`);
    console.log(`🧀 Toppings: ${argv.toppings.join(', ')}`);
  }
};

Bước 3: Gắn nó vào app.js. Bạn sẽ bất ngờ vì app.js giờ đây vô cùng gọn gàng:

// file: app.js
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');

yargs(hideBin(process.argv))
  // Gọi hàm commandDir và trỏ tới thư mục chứa các lệnh
  .commandDir('cmds') 
  .demandCommand(1, 'Bạn cần nhập ít nhất 1 lệnh để chạy tool này!')
  .strict()
  .help()
  .parse();

Hàm .commandDir('cmds') là một phép thuật! Bất cứ khi nào bạn tạo thêm một file mới trong thư mục cmds (ví dụ cancel.js, tracking.js), yargs sẽ tự động quét và load lệnh đó vào CLI của bạn mà bạn không cần phải sửa file app.js thêm một dòng nào nữa.

3. Tút lại nhan sắc cho Menu Help

Mặc định, khi gõ --help, giao diện đã khá ổn. Nhưng bạn có thể làm nó ngầu hơn, giống các tool chuyên nghiệp bằng cách thêm phần giới thiệu (Usage) và lời kết (Epilog).

Sửa lại phần cuối của app.js:

yargs(hideBin(process.argv))
  .commandDir('cmds')
  .usage('Cách dùng: $0 <command> [options]') // $0 đại diện cho tên file chạy
  .epilog('© 2024 - Được phát triển bởi [Tên Bạn]. Chúc bạn code vui vẻ!')
  .alias('h', 'help') // Cho phép dùng -h thay vì --help
  .alias('v', 'version')
  .strict()
  .help()
  .parse();

Tổng kết Bài 2

Đến đây, bạn đã hoàn toàn làm chủ được cách tổ chức mã nguồn của một công cụ CLI chuyên nghiệp. Bạn có thể tự tin xây dựng một công cụ có hàng chục tính năng khác nhau mà code vẫn cực kỳ dễ bảo trì, dễ đọc nhờ sức mạnh của commandDir và cơ chế xác thực dữ liệu chặt chẽ của yargs.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí