Source: Dependency.js

'use strict'

/**
 * Dependencies
 * @ignore
 */
const Model = require('../data/Model')

/**
 * Constants
 * @ignore
 */
const ARROW_ARG = /^([^\(]+?)=>/
const FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m
const FN_ARG_SPLIT = /,/
const FN_ARG = /^\s*(_?)(\S+?)\1\s*$/
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg

/**
 * Dependency
 *
 * @class
 * Instances of Dependency are self-validating containers for
 * dependencies registered on the injector.
 *
 * @param {Object} descriptor - Dependency descriptor
 *
 * @extends Model
 */
class Dependency extends Model {

  /**
   * Extract Dependencies
   * Adapted from AngularJS.
   * @private
   */
  static extractDependencies (fn) {
    let str = fn.toString().replace(STRIP_COMMENTS, '')
    let match = str.match(ARROW_ARG) || str.match(FN_ARGS)
    let args = match[1].split(FN_ARG_SPLIT)
    let result = args.map(str => str.trim())
    return result[0] !== '' ? result : []
  }

  /**
   * extractDependencies
   *
   * @description
   * Parse dependencies and store them as an array of dependency
   * names on this instance.
   *
   */
  extractDependencies () {
    if (this.fn && !this.dependencies) {
      this.dependencies = Dependency.extractDependencies(this.fn)
    }
  }

  /**
   * get schema
   * @private
   */
  static get schema () {

    /**
     * conform
     *
     * validation function for factory function and value on schema
     */
    function conform (value, instance) {
      // if there is a value present then dependency is valid
      if (instance.value) {
        return true
      }

      // check if there is a function present on the dependency
      return instance.fn
        ? typeof instance.fn === 'function'
        : false
    }

    // a standardised conformer property for factory function (fn)
    // and value on schema
    let conformer = {
      type: 'any',
      conform,
      messages: {
        conform: 'function or value is required'
      }
    }

    /**
     * Dependency schema
     */
    return {
      name: { type: 'string', required: true },
      type: { type: 'string', required: true },
      plugin: { type: 'string', required: true },
      fn: conformer,
      value: conformer,
      dependencies: { type: 'array' }
    }
  }

}

/**
 * Exports
 */
module.exports = Dependency