all files / src/ Config.js

100% Statements 44/44
100% Branches 30/30
88.89% Functions 8/9
100% Lines 44/44
1 statement Ignored     
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156                    14×                 14×                                                                                                                                                     12× 22×   22×       16×                       19×          
'use strict';
 
const inArray = require('in-array');
const isPlainObj = require('is-plain-obj');
const Parser = require('./Parser');
const path = require('path');
 
class Config {
 
  /**
   * Constructor
   * @param  {Object|String} passedConfigParam Object or string with desired configuration
   */
  constructor(passedConfigParam) {
    this.arrayRules = [
      'valid-values-author',
      'valid-values-private',
      'no-restricted-dependencies',
      'no-restricted-devDependencies',
      'no-restricted-pre-release-dependencies',
      'no-restricted-pre-release-devDependencies'
    ];
 
    this.passedConfigParam = passedConfigParam;
  }
 
  /**
   * Gets the config
   * @return {Object} Config object
   */
  get() {
    if (this._isConfigPassed(this.passedConfigParam)) {
      const passedConfig = this._getPassedConfig(this.passedConfigParam);
      let extendsConfig = {};
 
      if (passedConfig.hasOwnProperty('extends')) {
        extendsConfig = this._getExtendsConfig(passedConfig.extends);
      }
 
      return Object.assign({}, extendsConfig, passedConfig.rules);
    } else {
      throw new Error('No configuration passed');
    }
  }
 
  /**
   * Checks whether config has been passed or not
   * @param  {Object|String}  passedConfig    Object or string with desired configuration
   * @return {Boolean}                        True if config is present, false if it isn't
   */
  _isConfigPassed(passedConfig) {
    const noKeysLength = 0;
 
    return typeof passedConfig !== 'undefined' && Object.keys(passedConfig).length !== noKeysLength;
  }
 
  /**
   * Loads the config
   * @param  {Object|String} passedConfig  File path if string. Config object also accepted.
   * @return {Object}                         Config JSON
   */
  _getPassedConfig(passedConfig) {
    if (typeof passedConfig === 'string') {
      const parser = new Parser();
      let configFile = passedConfig;
 
      if (!path.isAbsolute(passedConfig)) {
        configFile = path.join(process.cwd(), passedConfig);
      }
 
      const rcFileObj = parser.parse(configFile);
 
      this._validateConfig(rcFileObj);
 
      return rcFileObj;
    }
 
    return passedConfig;
  }
 
  /**
   * Gets configuration from a extends config module
   * @param  {String} moduleName  Name of the configuration module
   * @return {Object}             Configuration object
   */
  _getExtendsConfig(moduleName) {
    const configObj = this._getExtendsConfigModule(moduleName);
 
    this._validateConfig(configObj);
 
    return configObj.rules;
  }
 
  /**
   * Loads extends config module
   * @param  {String} moduleName  Name of the configuration module
   * @return {Object}             Configuration object
   */
  _getExtendsConfigModule(moduleName) {
    /* istanbul ignore next */
    return require(path.join(process.cwd(), moduleName));
  }
 
  /**
   * Validates config object
   * @param  {Object} rcFileObj   Object version of .npmpackagejsonlintrc file
   * @return {boolean}            True if validate config is successful
   */
  _validateConfig(rcFileObj) {
    if (rcFileObj.hasOwnProperty('rules')) {
      this._validateRulesConfig(rcFileObj.rules);
    } else {
      throw new Error('`rules` object missing in config');
    }
 
    return true;
  }
 
  /**
   * Validates rules object
   * @param  {Object}     rulesObj   Object version of .npmpackagejsonlintrc file
   * @return {boolean}               True if validate config is successful
   */
  _validateRulesConfig(rulesObj) {
    for (const rule in rulesObj) {
      const ruleConfig = rulesObj[rule];
 
      if (Array.isArray(ruleConfig) && inArray(this.arrayRules, rule)) {
        if (typeof ruleConfig[0] !== 'string' || this._isRuleValid(ruleConfig[0])) {
          throw new Error(`${rule} - first key must be set to "error", "warning", or "off". Currently set to ${ruleConfig[0]}`);
        }
 
        if (!Array.isArray(ruleConfig[1])) {
          throw new Error(`${rule} - second key must be set an array. Currently set to ${ruleConfig[1]}`);
        }
      } else if (typeof ruleConfig !== 'string' || this._isRuleValid(ruleConfig)) {
        throw new Error(`${rule} - must be set to "error", "warning", or "off". Currently set to ${ruleConfig}`);
      }
    }
 
    return true;
  }
 
  /**
   * Validates the first key of an array type rule
   * @param  {String}  key Error type of the rule
   * @return {Boolean}     True if the rule is valid. False if the rule is invalid.
   */
  _isRuleValid(key) {
    return typeof key === 'string' && key !== 'error' && key !== 'warning' && key !== 'off';
  }
 
}
 
module.exports = Config;