Skip to content

sugoroku-y/optionalist

Repository files navigation

optionalist

The commandline parser for TypeScript.

optionalistはTypeScript向けに作られたコマンドラインパーザーです。

NPM version NPM monthly download NPM total download MIT License

使い方

optionalist.parseにコマンドラインの詳細を渡して、解析した結果を受け取ります。

const options = optionalist.parse({
  [optionalist.helpString]: {
    describe: 'The description for command.',
    showUsageOnError: true,
  },
  help: {
    type: 'boolean',
    alias: ['?', 'h'],
    alone: true,
    describe: 'Show this help.',
  },
  init: {
    type: 'boolean',
    alone: true,
    describe: 'Initialize your project.',
  },
  output: {
    type: 'string',
    required: true,
    describe: 'Specify the filename to output.',
    example: 'output_filename',
  },
  config: {
    default: path.resolve('config.json'),
    describe: 'Specify the configuration file for your project.',
    example: 'config_filename',
  },
  watch: {
    type: 'boolean',
    describe: 'Specify when you want to set the watch mode.',
  },
  timeout: {
    type: 'number',
  },
  [optionalist.unnamed]: {
    example: 'script_filename',
    describe: 'Specify the script filename(s) to execute.',
  },
} as const);
options.help ? options : options.init ? options : options;
// この時点でのoptionsは
// const options:
//   | {
//       readonly help: true;
//       readonly init?: never;
//       readonly output?: never;
//       readonly config?: never;
//       readonly watch?: never;
//       readonly timeout?: never;
//       readonly [unnamed]?: never;
//       readonly [helpString]: string;
//     }
//   | {
//       readonly init: true;
//       readonly help?: never;
//       readonly output?: never;
//       readonly config?: never;
//       readonly watch?: never;
//       readonly timeout?: never;
//       readonly [unnamed]?: never;
//       readonly [helpString]: string;
//     }
//   | {
//       readonly [unnamed]: readonly string[];
//       readonly [helpString]: string;
//       readonly help?: never;
//       readonly init?: never;
//       readonly output: string;
//       readonly config: string;
//       readonly watch?: true;
//       readonly timeout?: number;
//     };

受け取った結果は自動的に型付けされ、もし指定されていればデフォルト値が設定された状態になっています。

// --helpが指定されたとき
if (options.help) {
  options;
  // このスコープでのoptionsは
  // const options: {
  //   readonly help: true;
  //   readonly init?: never;
  //   readonly output?: never;
  //   readonly config?: never;
  //   readonly watch?: never;
  //   readonly timeout?: never;
  //   readonly [unnamed]?: never;
  //   readonly [helpString]: string;
  // };

  // [optionalist.helpString]はコマンドの説明用文字列を返す。
  console.log(options[optionalist.helpString]);
  // この例では以下のような文字列になる。
  // Version: sample 0.0.1
  // Usage:
  //   node sample --output output_filename [--config config_filename] [--watch] [--timeout parameter] [--] [script_filename...]
  //   node sample --help
  //   node sample --init

  // Description:
  //   The description for command.

  // Options:
  //   --help, -?, -h
  //     Show this help.
  //   --init
  //     Initialize your project.
  //   --output output_filename
  //     Specify the filename to output.
  //   --config config_filename
  //     Specify the configuration file for your project.
  //   --watch
  //     Specify when you want to set the watch mode.
  //   --timeout parameter
  //   [--] [script_filename...]
  //     Specify the script filename(s) to execute.
  //
  process.exit(0);
}

options.init ? options : options;
// この時点でのoptionsは
// const options:
//   | {
//       readonly init: true;
//       readonly help?: never;
//       readonly output?: never;
//       readonly config?: never;
//       readonly watch?: never;
//       readonly timeout?: never;
//       readonly [unnamed]?: never;
//       readonly [helpString]: string;
//     }
//   | {
//       readonly [unnamed]: readonly string[];
//       readonly [helpString]: string;
//       readonly help?: never;
//       readonly init?: never;
//       readonly output: string;
//       readonly config: string;
//       readonly watch?: true;
//       readonly timeout?: number;
//     };

// --initが指定されたとき
if (options.init) {
  options;
  // このスコープでのoptionsは
  // const options: {
  //   readonly init: true;
  //   readonly help?: never;
  //   readonly output?: never;
  //   readonly config?: never;
  //   readonly watch?: never;
  //   readonly timeout?: never;
  //   readonly [unnamed]?: never;
  //   readonly [helpString]: string;
  // };

  initializeProject();
  process.exit(0);
}

options;
// この時点でのoptionsは
// const options: {
//   readonly [unnamed]: readonly string[];
//   readonly [helpString]: string;
//   readonly help?: never;
//   readonly init?: never;
//   readonly output: string;
//   readonly config: string;
//   readonly watch?: true;
//   readonly timeout?: number;
// };

// プロパティはそれぞれ指定された型になっている。

loadConfigfile(options.config);

for (const file of options[optionalist.unnamed]) {
  executeFile(file, options.output);
}

if (options.watch) {
  const list = options[optionalist.unnamed].slice(0);
  watch(list, file => executeFile(file, options.output));
}

コマンドラインの詳細

コマンドラインの詳細はoptionalist.parseの第1引数に、以下のように指定します。

  • [optionalist.helpString]: コマンド全体に関する指定を行います。

    • describe: ヘルプ用文字列で表示される、コマンドの説明を指定します。

      省略時にはコマンドの説明が表示されません。

    • showUsageOnError: コマンドラインでの指定に不備があった場合、ヘルプ用文字列を表示して終了するのであればtrueを指定します。

  • [name: string]: 各オプションの詳細を指定します。

    実際にコマンドラインパラメーターとして指定するには、nameが一文字だけの場合は-x、2文字以上の場合は--xxxのように指定します。

    • type: オプションの型を指定します。

      'string''number''boolean'のいずれかを指定します。

      'string'の場合には文字列型、'number'の場合には数値型、‘boolean'の場合には真偽値型になります。

      省略時には'string'が指定されたものと見なします。

    • constraints: 文字列型、数値型の場合、指定できる値への制約を指定します。

      • 配列が指定された場合は、その中の1つが指定されていないとエラーになります。(type: 'number'、もしくはtype: 'string')

      • minプロパティを持つオブジェクトが指定された場合は、min未満の値が指定されるとエラーになります。(type: 'number')

        maxもしくはmaxExclusiveと同時に指定できます。

      • maxプロパティを持つオブジェクトが指定された場合は、maxより大きい値が指定されるとエラーになります。(type: 'number')

        minもしくはminExclusiveと同時に指定できます。

      • minExclusiveプロパティを持つオブジェクトが指定された場合は、minExclusive以下の値が指定されるとエラーになります。(type: 'number')

        maxもしくはmaxExclusiveと同時に指定できます。

      • maxExclusiveプロパティを持つオブジェクトが指定された場合は、maxExclusive以上の値が指定されるとエラーになります。(type: 'number')

        minもしくはminExclusiveと同時に指定できます。

      • 正規表現が指定された場合は、その正規表現にマッチしない値が指定されるとエラーになります。(type: 'string')

      type: 'boolean'の場合には指定できません。

    • example: ヘルプ用文字列でパラメーターとして使用される文字列を指定します。

      たとえば{alpha: {example: 'filename'}}と指定した場合、ヘルプ用文字列では--alpha filenameのように使用されます。

      type: 'boolean'の場合には指定できません。

    • alone: 単独で指定するオプションのときにtrueを指定します。

      ほかのオプションは名前付き、無名にかかわらず指定するとエラーになります。

    • required: 指定が必須なオプションのときにtrueを指定します。

      このオプションが指定されていないとエラーになります。

      type: 'boolean'の場合には指定できません。

    • default: このオプションが省略されたときに使用するデフォルト値を指定します。

      デフォルト値にはtype: 'string'の場合には文字列を、type: 'number'の場合には数値を指定しなければなりません。

      type: 'boolean'の場合には指定できません。

    • alias: オプションの別名を指定します。

      実際にコマンドラインで使用する場合には、nameと同様に一文字だけの場合は-x、2文字以上の場合は--xxxのように指定します。

      複数指定するには配列で指定します。

    • describe: オプションの説明を指定します。

      ヘルプ用文字列で使用されます。

    オブジェクトではなく文字列、数値、trueを指定することも可能です。

    • 文字列を指定すると{default: その文字列}を指定したように扱います。
    • 数値を指定すると、{type: 'number', default: その数値}を指定したように扱います。
    • trueを指定すると{type: 'boolean'}を指定したように扱います。
  • [optionalist.unnamed]: 無名オプションの詳細を指定します。

    • example: ヘルプ用文字列の中で無名オプションを指す名前を指定します。

    • describe: ヘルプ用文字列の中で使用される無名オプションの説明を指定します。

    • min: 無名オプションの最小個数を指定します。

      省略時には最小個数のチェックを行いません。

    • max: 無名オプションの最大個数を指定します。

      省略時には最大個数のチェックを行いません。

解析するコマンドライン

parseの第2引数には解析するコマンドラインを指定します。

省略時にはprocess.argvの3番目から解析を開始します。

配列だけでなくIterable<string>を指定することが可能です。

解析結果

返値には解析結果を指定されたオプションの名前と型を持つオブジェクトにして返します。

aloneを指定したオプションがある場合は、aloneを指定したオプションだけの型と、それ以外のオプションだけの型のUnion型になっています。

const options = optionalist.parse({aaa: {alone: true, type: 'boolean'}, bbb: '', ccc: 0});
// options:
// | {
//   aaa: true; // aaaだけが存在する。
//   bbb?: never;
//   ccc?: never;
//   [unnamed]?: never; // unnamedもなし
//   [helpString]: string; // helpStringだけはヘルプのため常に追加
// }
// | {
//   aaa?:never; // aaaはなし
//   bbb: string;
//   ccc: number;
//   [unnamed]: string[];
//   [helpString]: string; // helpStringだけはヘルプのため常に追加
// }

aloneなオプションが指定された場合、その他のオプションは指定されていないためundefinedになっていることに注意してください。

逆にその他のオプションを使うときはすべてのaloneなオプションが指定されていないことを確認してからでないと、通常のプロパティのようにアクセスできません。これによりaloneなオプションの処理漏れが防げます。

if (options.aaa) {
  assert(options.bbb === undefined);
  assert(options.ccc === undefined);
  process.exit(0);
}

const { bbb,ccc } = options;
assert(typeof bbb === 'string');
assert(typeof ccc === 'number');

通常のオプションはoptionalになっていますが、必須のオプション、およびデフォルト値の指定されたオプションは非optionalとなっています。

  const options = optionalist.parse({
    aaa: {
      type: 'string',
    },
    bbb: {
      required: true,
    },
    ccc: {
      default: 'ccc',
    },
  });
  const { aaa, bbb, ccc } = options;
  // aaaにはrequiredもdefaultも指定されていないのでoptional
  assert(aaa === undefined || typeof aaa === 'string');
  // bbbにはrequiredが指定されているので非optional
  assert(typeof bbb === 'string');
  // cccにはdefaultが指定されているので非非optional
  assert(typeof ccc === 'string');

無名オプションは[optionalist.unnamed]stringの配列として取得できます。

for (const arg of options[optionalist.unnamed]) {
  // ...
}

ヘルプ

使い方を表示したい場合には、指定された情報から自動的に生成したヘルプ用文字列が[optionalist.helpString]で取得できます。

またparseの第1引数の[optionalist.helpString]showUsageOnErrorを指定していると、コマンドライン引数にエラーがあったとき、使い方を表示して終了します。

const options = optionalist.parse({
  // ...
  help: {alone: true, type: 'boolean'},
  [optionalist.helpString]: {
    // コマンドライン引数での指定にエラーがあれば使い方を表示して終了
    showUsageOnError: true,
  },
});

if (options.help) {
  // --helpが指定されたらヘルプを表示して終了
  console.log(options[optionalist.helpString]);
  process.exit(0);
}

VS Codeなどの型情報が表示されるエディターを使用しているなら、型情報として各パラメーターの説明が表示されます。

const options = optionalist.parse({
  // ...
  output: {
    type: 'string',
    required: true,
    describe: 'Specify the filename to output.',
    example: 'output_filename',
  },
  // ...
});

のように記述していれば

  executeFile(file, options.output);

と記述したときに、outputにマウスカーソルを合わせれば、

(property) output: string & {
    [description]: ["--output output_filename: Specify the filename to output.", "is specified always."];
}

のように表示されます。

実際にコマンドラインパラメーターとして指定する際の文字列 --output output_filename および説明 Specify the filename to output. と、requiredが指定されているので必須パラメーターであることを示す is specified always. が表示されています。同様にalonedefaultなどを指定した場合も説明が追加されます。

ただし実際にこの説明文をプロパティとして保持しているわけではないため、アクセスできないことに注意してください。

About

The commandline parser for TypeScript

Resources

License

Stars

Watchers

Forks

Packages

No packages published