{repeat} = require './helpers'
{repeat} = require './helpers'
コマンドラインからオプションフラグを解析するための単純なOptionParserクラス。次のように使用します。
parser = new OptionParser switches, helpBanner
options = parser.parse process.argv
最初の非オプションはファイル(およびファイルオプション)リストの開始と見なされ、それ以降のすべての引数は解析されないままになります。
coffee
コマンドはsrc/command.coffee
でOptionParserのインスタンスを使用してコマンドライン引数を解析します。
exports.OptionParser = class OptionParser
使用可能なオプションのリストで初期化します。形式は次のとおりです。
[short-flag, long-flag, description]
使用法のヘルプのオプションのバナーを指定することもできます。
constructor: (ruleDeclarations, @banner) ->
@rules = buildRules ruleDeclarations
引数リストを解析し、指定されたすべてのオプションを含むoptions
オブジェクトを取り込み、返します。最初の非オプション引数の後のオプションは引数として扱われます。options.arguments
は残りの引数を含む配列になります。これは、各フラグにコールバックアクションを添付できる多くのオプションパーサーよりも単純なAPIです。代わりに、オプションオブジェクトを解釈する必要があります。
parse: (args) ->
CoffeeScriptオプションパーサーは少し奇妙です;最初の非オプション引数の後のオプションは、非オプション引数自体として扱われます。オプション引数は、マージされたフラグを複数のフラグに展開することによって正規化されます。これにより、-wl
を--watch --lint
と同じにすることができます。shebang(#!
)行を持つ実行可能スクリプトは、行#!/usr/bin/env coffee
または#!/absolute/path/to/coffee
を使用する必要があることに注意してください。これはLinuxでは失敗するため、その後ろに--
引数を付けるべきではありません(#3946を参照)。
{rules, positional} = normalizeArguments args, @rules.flagDict
options = {}
argument
フィールドはnormalizeArguments
によって破壊的にルールインスタンスに追加されます。
for {hasArgument, argument, isList, name} in rules
if hasArgument
if isList
options[name] ?= []
options[name].push argument
else
options[name] = argument
else
options[name] = true
if positional[0] is '--'
options.doubleDashed = yes
positional = positional[1..]
options.arguments = positional
options
このOptionParserのヘルプテキストを返し、--help
などに使用可能なすべてのオプションを一覧表示して説明します。
help: ->
lines = []
lines.unshift "#{@banner}\n" if @banner
for rule in @rules.ruleList
spaces = 15 - rule.longFlag.length
spaces = if spaces > 0 then repeat ' ', spaces else ''
letPart = if rule.shortFlag then rule.shortFlag + ', ' else ' '
lines.push ' ' + letPart + rule.longFlag + spaces + rule.description
"\n#{ lines.join('\n') }\n"
コマンドラインとそれらのルールのオプションフラグの正規表現マッチャー。
LONG_FLAG = /^(--\w[\w\-]*)/
SHORT_FLAG = /^(-\w)$/
MULTI_FLAG = /^-(\w{2,})/
引数を持つオプションのルールの長いフラグ部分に一致します。process.argvには適用されません。
OPTIONAL = /\[(\w+(\*?))\]/
オプションルールのリストを作成して返します。オプションのショートフラグが指定されていない場合は、null
をパディングして除外します。
buildRules = (ruleDeclarations) ->
ruleList = for tuple in ruleDeclarations
tuple.unshift null if tuple.length < 3
buildRule tuple...
flagDict = {}
for rule in ruleList
shortFlag
は、ルールで指定されていない場合は null です。
for flag in [rule.shortFlag, rule.longFlag] when flag?
if flagDict[flag]?
throw new Error "flag #{flag} for switch #{rule.name}
was already declared for switch #{flagDict[flag].name}"
flagDict[flag] = rule
{ruleList, flagDict}
短縮フラグ -o
、長形式フラグ --output [DIR]
、およびオプションの機能の記述からルールを作成します。
buildRule = (shortFlag, longFlag, description) ->
match = longFlag.match(OPTIONAL)
shortFlag = shortFlag?.match(SHORT_FLAG)[1]
longFlag = longFlag.match(LONG_FLAG)[1]
{
name: longFlag.replace /^--/, ''
shortFlag: shortFlag
longFlag: longFlag
description: description
hasArgument: !!(match and match[1])
isList: !!(match and match[2])
}
normalizeArguments = (args, flagDict) ->
rules = []
positional = []
needsArgOpt = null
for arg, argIndex in args
スクリプトに指定された前の引数が、その引数として次のコマンドライン引数を使用するオプションの場合は、argument
フィールドを持つオプションのルールのコピーを作成します。
if needsArgOpt?
withArg = Object.assign {}, needsArgOpt.rule, {argument: arg}
rules.push withArg
needsArgOpt = null
continue
multiFlags = arg.match(MULTI_FLAG)?[1]
.split('')
.map (flagName) -> "-#{flagName}"
if multiFlags?
multiOpts = multiFlags.map (flag) ->
rule = flagDict[flag]
unless rule?
throw new Error "unrecognized option #{flag} in multi-flag #{arg}"
{rule, flag}
複数フラグにある最後のフラグのみが引数を持つことができます。
[innerOpts..., lastOpt] = multiOpts
for {rule, flag} in innerOpts
if rule.hasArgument
throw new Error "cannot use option #{flag} in multi-flag #{arg} except
as the last option, because it needs an argument"
rules.push rule
if lastOpt.rule.hasArgument
needsArgOpt = lastOpt
else
rules.push lastOpt.rule
else if ([LONG_FLAG, SHORT_FLAG].some (pat) -> arg.match(pat)?)
singleRule = flagDict[arg]
unless singleRule?
throw new Error "unrecognized option #{arg}"
if singleRule.hasArgument
needsArgOpt = {rule: singleRule, flag: arg}
else
rules.push singleRule
else
これは位置引数です。
positional = args[argIndex..]
break
if needsArgOpt?
throw new Error "value required for #{needsArgOpt.flag}, but it was the last
argument provided"
{rules, positional}