• 跳转到 … +
    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,})/
  • §

    匹配具有参数的选项的规则的 long flag 部分。不应用于 process.argv 中的任何内容。

    OPTIONAL   = /\[(\w+(\*?))\]/
  • §

    构建并返回选项规则列表。如果可选的 short-flag 未指定,则通过用 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 short flag、--output [DIR] long flag 和选项功能的描述构建规则。

    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}