created: 2022-07-04T03:25:00.010Z

class-validatorとclass-transformerを使ってコマンドラインオプションを受け取る

nodejsのコマンドラインパーサーは色々あるが、class-validatorclass-transformer を使ってパースしてみたら、新しく既存のコマンドラインパーサーを使うよりも覚えることも少なくて型安全に引数が受け取れてよかった。

受け取る引数をこんな感じでクラス定義する。

import { IsOptional, IsBoolean, IsString } from "class-validator";

export class Option {
  @IsOptional()
  @IsString()
  tableNamePattern?: string;

  @IsOptional()
  @IsBoolean()
  dryrun?: boolean;
  
  // メソッドもかける
  isToIgnore() {
    ....
  }
}

実装

パースする処理も20行ほどで済む。

import { plainToClass, ClassConstructor } from "class-transformer";
import { validateSync } from "class-validator";
import { camelCase } from "change-case"
import { formatErrors } from './utility';

export const loadOption = <T>(Cls: ClassConstructor<T>, v: unknown) => {
  const obj = plainToClass(Cls, v);
  const errors = validateSync(obj as any);
  if (errors.length) {
    throw OptionError(formatErrors("Faliled to load options.", errors));
  } else {
    return obj as T;
  }
};

export const parseArgs = (argv: string[]) => {
  const entry = [];
  for (const opt of argv) {
    const [, name, param] = opt.match(/--([\w-]+)=(\S+)/) || [];
    if (name?.length && param?.length) {
      entry.push([camelCase(name), param]);
      continue
    }
    const [, flag] = opt.match(/--([\w-]+)/) || [];
    if (flag?.length) {
      entry.push([camelCase(flag), true]);
      continue
    }
  }
  return Object.fromEntries(entry);
};


export class OptionError extends Error {};
怒られの作法 ――日本一トラブルに巻き込まれる編集者の人間関係術 (単行本)
[ad] 怒られの作法 ――日本一トラブルに巻き込まれる編集者の人間関係術 (単行本)
草下 シンヤ (単行本(ソフトカバー))