• 跳至… +
    browser.coffee cake.coffee coffeescript.coffee command.coffee grammar.coffee helpers.coffee index.coffee lexer.coffee nodes.coffee optparse.coffee register.coffee repl.coffee rewriter.coffee scope.litcoffee sourcemap.litcoffee
  • optparse.coffee

  • §
    {repeat} = require './helpers'
  • §

    一個簡單的 OptionParser 類別,用於從命令列解析選項標記。使用方式如下

    parser  = new OptionParser switches, helpBanner
    options = parser.parse process.argv
    

    第一個非選項被視為檔案 (和檔案選項) 清單的開頭,所有後續引數都保持未解析。

    coffee 命令使用 OptionParser 的一個實例,在 src/command.coffee 中解析其命令列引數。

    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}