示例#1
0
 constructor(...args) {
   args = to.arguments({
     str: '',
     lineno: ''
   }, ...args)
   to.extend(this, args)
   this.raw = this.str
   this.indent = to.indentLevel(this.str)
 }
示例#2
0
文件: index.js 项目: tjbenton/docs
  setOptions(options) {
    options = to.arguments({
      language: {
        prefix: '@',

        // header comment style
        // @note {10} only 1 of these can be used per file
        header: { start: '////', line: '///', end: '////', type: 'header' },

        // body comment style
        body: { start: '', line: '///', end: '', type: 'body' },

        // inline comment style
        inline: { start: '', line: '///#', end: '', type: 'inline' },

        // this is used for any interpolations that might occur in annotations.
        // I don't see this needing to change but just incase I'm making it a setting.
        // @note {10} This setting is used to create a RegExp so certain characters need to be escaped
        interpolation: { start: '\\${', end: '}' },
      },
      type: undefined,
      blank_lines: 4,
      indent: true,
      annotations: {},
      sort: [],
      log: logger
    }, arguments)

    let { annotations, type, log, ...rest } = options
    this.log = log
    this.options = rest
    this.api = new AnnotationApi({ annotations, type })

    // this is used to pass to the annotations when they're called
    this.annotation_options = { log: this.log, options: this.options }

    const { language } = this.options

    this.comment_values = to.flatten([
      ...to.values(language.header, '!type'),
      ...to.values(language.body, '!type'),
      ...to.values(language.inline, '!type')
    ])
    this.comment_values = to.unique(this.comment_values).filter(Boolean)

    {
      let { alias, parse } = this.api.annotations
      parse = to.keys(parse)
      const reverse_alias_list = to.reduce(alias, (previous, { key, value }) => {
        value = value
          .filter((_alias) => !is.in(parse, _alias))
          .reduce((a, b) => to.extend(a, { [b]: key }), {})
        return to.extend(previous, value)
      }, {})
      const regex = new RegExp(`^\s*${language.prefix}(?:(${to.keys(reverse_alias_list).join('|')})|(${parse.join('|')}))\\b\\s*`)
      const available = to.unique(to.reduce(this.api.annotations, (previous, { value }) => previous.concat(to.keys(value)), []))
      this.annotations_list = { available, reverse_alias_list, regex }
    }

    this.options.order = sort(this.options.sort, {
      extras: this.annotations_list.available,
      amount: 3
    })
    this.options.order.push('inline') // this is for the inline part of the annotation
  }
示例#3
0
  setOptions(options) { // eslint-disable-line
    options = to.arguments({
      content: '',
      lineno: 0,
      comment: this.options.comment || { start: '', line: '///', end: '', type: undefined }, // the default comment style to look for
      blank_lines: this.options.blank_lines || default_options.blank_lines,
      // determins if all the extra line info will be returned or if it will just return strings
      verbose: !is.undefined(this.options.verbose) ? this.options.verbose : false,
      // determins if the comment should be stripped from the line
      strip: !is.undefined(this.options.strip) ? this.options.strip : false,
      // if true this option will only return the first token
      restrict: !is.undefined(this.options.restrict) ? this.options.restrict : false,
      // determins if the code below should stop parsing if the indent level is less than the starting indent level
      indent: !is.undefined(this.options.indent) ? this.options.indent : true,
      offset: 0,
    }, arguments)

    { // parse the comment to ensure the settings are valid, and attempt to update them
      // to be valid if they aren't valid
      let { start, line, single, end, type } = options.comment

      single = single || line
      // this ensures there aren't any errors while looking comment lines
      // because `''` will always have an index of `0`
      if (single === '') {
        single = undefined
      }

      if (is.any.in([ single, '' ], start, end)) {
        start = end = undefined
      }

      if (!single && is.any.undefined(start, end)) {
        throw new Error("You must set the start and end comment style if you don't specify a single comment")
      } else if (!start && !end && !single) {
        throw new Error('You must set the single comment or the start and end comment')
      } else if (is.all.existy(single, start, end) && (start.length <= single.length || start.end <= single.length)) {
        throw new Error('The start and end comments must be longer than the single comment')
      }

      options.comment = { start, single, end, type }
      this.is_multi = is.all.truthy(start, end)
      this.is_same_multi = this.is_multi && start === end
    }

    { // set the lineno to start with
      const { i, index, lineno, start_at, ...rest } = options
      this.lineno = i || index || start_at || lineno || 0
      options = rest
    }


    let stash
    { // set the string to use
      let { str, string, source, code, content, contents, ...rest } = options
      stash = str || string || source || code || content || contents || ''
      options = rest
    }

    // update the options
    this.options = options

    // holds the parsed tokens
    this.tokens = []

    this.current_blank_lines = 0

    // update the stash
    this.stash = this.getStash(stash)

    // update the iterator to use
    this.iterator = to.entries(this.stash, this.lineno)
  }
示例#4
0
  run(type, ...options) {
    {
      const base = { contents: [], start: -1, end: -1 }
      options = to.arguments({
        annotation: { name: '', alias: '', ...base },
        file: { path: '', name: '', type: '', ...base },
        comment: { ...base },
        code: { ...base },
      }, ...options)
    }

    // /// @name add
    // /// @page annotation
    // /// @description Allows you to add a different annotation from within a annotation
    // /// @arg {string} name - the name of the annotation you want to add
    // /// @arg {string} str - information that is passed to the annotation
    // const add = (name, contents) => {
    //   contents = to.normalize(contents)
    //   return this.run({
    //     annotation: {
    //       name,
    //       alias: is.in(this.annotations.alias, name) ? this.annotations.alias[name] : [],
    //       line: to.normalize(contents[0]),
    //       contents,
    //       start: null,
    //       end: null
    //     },
    //     annotation_types,
    //     ...block,
    //     log
    //   })
    // }

    {
      const list = this.annotations_list
      const { annotation, file } = options
      const fn = (list[file.type] || {})[annotation.name]
      let default_fn = (list.default || {})[annotation.name]

      if (fn && default_fn && (default_fn = default_fn[type])) {
        options.default = () => default_fn.call(options)
      }
    }

    let details = options

    try {
      const fn = this.getAnnotationFn(options.annotation.name, type)

      if (type === 'parse') {
        details = copyInvisible(options.annotation, options)
      } else if (type === 'resolve') {
        details = copyInvisible(options.parsed[options.annotation.name], options)
      }

      let result
      try {
        result = is.function(fn) ? fn.call(details) : fn
      } catch (e) {
        throw e
      }

      if (type === 'parse' && !is.any.undefined(details, result)) {
        // copy the details that were used to call the parse function command onto the result
        // as `details` this gives the resolve function the ability to access all of the information if needed
        if (is.array(result) && result.length === 1) {
          result = to.map(result, (obj) => copyInvisible(obj, { details }))
        }

        result = copyInvisible(result, { details })
      }

      return result
    } catch (e) {
      throw e
    }


    function copyInvisible(obj, value) {
      let _type = to.type(obj)

      switch (_type) {
        case 'array':
        case 'object':
          break
        default:
          let types = { String, Number, Boolean }
          let Type = types[_type.charAt(0).toUpperCase() + _type.slice(1)]
          obj = new Type(obj)
      }

      // loop over the data and set them to be getters on the
      // passed object. This makes them available but they don't show
      // up in the console and clutter up all the things
      for (var item in value) {
        if (value.hasOwnProperty(item)) {
          Object.defineProperty(obj, item, { value: value[item] })
        }
      }

      // set the prototype to the the passed data
      Object.defineProperty(obj, 'prototype', { value })
      return obj
    }
  }