minimistJavaScript でCLI引数をパースするときに便利なパッケージです

CLIオプションはlongオプションの場合 --foo-bar のように指定しますが、interfaceのpropertyで使用する際には fooBar とcamelCaseになっていたほうが気持ちがいいです。 それをするためのラッパー処理を書きました

import minimist from "minimist";
 
type Kebab<T extends string, A extends string = ""> = T extends `${infer F}${infer R}`
  ? Kebab<R, `${A}${F extends Lowercase<F> ? "" : "-"}${Lowercase<F>}`>
  : A;
 
type KebabKeys<T> = Partial<{ [K in keyof T as K extends string ? Kebab<K> : K]: T[K] }>;
type Alias<T> = Partial<{ [K in keyof T as K extends string ? Kebab<K> : K]: string | string[] }>;
type IsRequired<T> = Partial<{ [K in keyof T as K extends string ? Kebab<K> : K]: boolean }>;
 
const toCamel = (str: string) => str.replace(/([-_][a-z])/gi, ($1) => $1.toUpperCase().replace("-", "").replace("_", ""));
 
interface Opts<T> {
  alias?: Alias<T>;
  default?: KebabKeys<T>;
  required?: IsRequired<T>;
}
 
/**
 * kebab-caseで指定されたCLIオプションをcamelCaseのフィールドに変換する
 * e.g. `--base-path /work` は `basePath: "/work"` になる
 */
export function parseArgs<T extends minimist.ParsedArgs>(args: string[], opts?: Opts<T>): T {
  const al: { [key: string]: string | string[] } = {};
  if (opts?.alias != null) {
    for (const [k, v] of Object.entries(opts.alias)) {
      al[k] = v;
    }
  }
 
  const argv = minimist<T>(args, {
    alias: al,
    default: opts?.default,
  }) as T;
 
  if (opts?.required != null) {
    for (const k of Object.keys(opts.required) as (keyof KebabKeys<T>)[]) {
      const isRequired = opts.required[k];
      if (isRequired && argv[k] == null) {
        throw new Error(`required parameter [${String(k)}] is missing`);
      }
    }
  }
 
  let t: any = {};
  Object.keys(argv).forEach((k) => {
    t[toCamel(k)] = argv[k];
  });
 
  return t;
}

使い方

interface Options {
  _: string[];
 
  workingDir: string;
  foo: string;
  no: number;
}
 
const args = parseArgs<Options>(process.argv.slice(2), {
  alias: {
    foo: ["f"],
    "working-dir": "w",
  },
  default: {
    foo: "bar",
    no: 1,
  },
  required: {
    "working-dir": true,
  },
});
 
console.log(JSON.stringify(args));