| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 | 
							- /**
 
-  * @fileoverview Create configurations for a rule
 
-  * @author Ian VanSchooten
 
-  */
 
- "use strict";
 
- //------------------------------------------------------------------------------
 
- // Requirements
 
- //------------------------------------------------------------------------------
 
- const Rules = require("../rules"),
 
-     loadRules = require("../load-rules");
 
- const rules = new Rules();
 
- //------------------------------------------------------------------------------
 
- // Helpers
 
- //------------------------------------------------------------------------------
 
- /**
 
-  * Wrap all of the elements of an array into arrays.
 
-  * @param   {*[]}     xs Any array.
 
-  * @returns {Array[]}    An array of arrays.
 
-  */
 
- function explodeArray(xs) {
 
-     return xs.reduce((accumulator, x) => {
 
-         accumulator.push([x]);
 
-         return accumulator;
 
-     }, []);
 
- }
 
- /**
 
-  * Mix two arrays such that each element of the second array is concatenated
 
-  * onto each element of the first array.
 
-  *
 
-  * For example:
 
-  * combineArrays([a, [b, c]], [x, y]); // -> [[a, x], [a, y], [b, c, x], [b, c, y]]
 
-  *
 
-  * @param   {array} arr1 The first array to combine.
 
-  * @param   {array} arr2 The second array to combine.
 
-  * @returns {array}      A mixture of the elements of the first and second arrays.
 
-  */
 
- function combineArrays(arr1, arr2) {
 
-     const res = [];
 
-     if (arr1.length === 0) {
 
-         return explodeArray(arr2);
 
-     }
 
-     if (arr2.length === 0) {
 
-         return explodeArray(arr1);
 
-     }
 
-     arr1.forEach(x1 => {
 
-         arr2.forEach(x2 => {
 
-             res.push([].concat(x1, x2));
 
-         });
 
-     });
 
-     return res;
 
- }
 
- /**
 
-  * Group together valid rule configurations based on object properties
 
-  *
 
-  * e.g.:
 
-  * groupByProperty([
 
-  *     {before: true},
 
-  *     {before: false},
 
-  *     {after: true},
 
-  *     {after: false}
 
-  * ]);
 
-  *
 
-  * will return:
 
-  * [
 
-  *     [{before: true}, {before: false}],
 
-  *     [{after: true}, {after: false}]
 
-  * ]
 
-  *
 
-  * @param   {Object[]} objects Array of objects, each with one property/value pair
 
-  * @returns {Array[]}          Array of arrays of objects grouped by property
 
-  */
 
- function groupByProperty(objects) {
 
-     const groupedObj = objects.reduce((accumulator, obj) => {
 
-         const prop = Object.keys(obj)[0];
 
-         accumulator[prop] = accumulator[prop] ? accumulator[prop].concat(obj) : [obj];
 
-         return accumulator;
 
-     }, {});
 
-     return Object.keys(groupedObj).map(prop => groupedObj[prop]);
 
- }
 
- //------------------------------------------------------------------------------
 
- // Private
 
- //------------------------------------------------------------------------------
 
- /**
 
-  * Configuration settings for a rule.
 
-  *
 
-  * A configuration can be a single number (severity), or an array where the first
 
-  * element in the array is the severity, and is the only required element.
 
-  * Configs may also have one or more additional elements to specify rule
 
-  * configuration or options.
 
-  *
 
-  * @typedef {array|number} ruleConfig
 
-  * @param {number}  0  The rule's severity (0, 1, 2).
 
-  */
 
- /**
 
-  * Object whose keys are rule names and values are arrays of valid ruleConfig items
 
-  * which should be linted against the target source code to determine error counts.
 
-  * (a ruleConfigSet.ruleConfigs).
 
-  *
 
-  * e.g. rulesConfig = {
 
-  *     "comma-dangle": [2, [2, "always"], [2, "always-multiline"], [2, "never"]],
 
-  *     "no-console": [2]
 
-  * }
 
-  * @typedef rulesConfig
 
-  */
 
- /**
 
-  * Create valid rule configurations by combining two arrays,
 
-  * with each array containing multiple objects each with a
 
-  * single property/value pair and matching properties.
 
-  *
 
-  * e.g.:
 
-  * combinePropertyObjects(
 
-  *     [{before: true}, {before: false}],
 
-  *     [{after: true}, {after: false}]
 
-  * );
 
-  *
 
-  * will return:
 
-  * [
 
-  *     {before: true, after: true},
 
-  *     {before: true, after: false},
 
-  *     {before: false, after: true},
 
-  *     {before: false, after: false}
 
-  * ]
 
-  *
 
-  * @param   {Object[]} objArr1 Single key/value objects, all with the same key
 
-  * @param   {Object[]} objArr2 Single key/value objects, all with another key
 
-  * @returns {Object[]}         Combined objects for each combination of input properties and values
 
-  */
 
- function combinePropertyObjects(objArr1, objArr2) {
 
-     const res = [];
 
-     if (objArr1.length === 0) {
 
-         return objArr2;
 
-     }
 
-     if (objArr2.length === 0) {
 
-         return objArr1;
 
-     }
 
-     objArr1.forEach(obj1 => {
 
-         objArr2.forEach(obj2 => {
 
-             const combinedObj = {};
 
-             const obj1Props = Object.keys(obj1);
 
-             const obj2Props = Object.keys(obj2);
 
-             obj1Props.forEach(prop1 => {
 
-                 combinedObj[prop1] = obj1[prop1];
 
-             });
 
-             obj2Props.forEach(prop2 => {
 
-                 combinedObj[prop2] = obj2[prop2];
 
-             });
 
-             res.push(combinedObj);
 
-         });
 
-     });
 
-     return res;
 
- }
 
- /**
 
-  * Creates a new instance of a rule configuration set
 
-  *
 
-  * A rule configuration set is an array of configurations that are valid for a
 
-  * given rule.  For example, the configuration set for the "semi" rule could be:
 
-  *
 
-  * ruleConfigSet.ruleConfigs // -> [[2], [2, "always"], [2, "never"]]
 
-  *
 
-  * Rule configuration set class
 
-  */
 
- class RuleConfigSet {
 
-     /**
 
-      * @param {ruleConfig[]} configs Valid rule configurations
 
-      */
 
-     constructor(configs) {
 
-         /**
 
-          * Stored valid rule configurations for this instance
 
-          * @type {array}
 
-          */
 
-         this.ruleConfigs = configs || [];
 
-     }
 
-     /**
 
-      * Add a severity level to the front of all configs in the instance.
 
-      * This should only be called after all configs have been added to the instance.
 
-      *
 
-      * @returns {void}
 
-      */
 
-     addErrorSeverity() {
 
-         const severity = 2;
 
-         this.ruleConfigs = this.ruleConfigs.map(config => {
 
-             config.unshift(severity);
 
-             return config;
 
-         });
 
-         // Add a single config at the beginning consisting of only the severity
 
-         this.ruleConfigs.unshift(severity);
 
-     }
 
-     /**
 
-      * Add rule configs from an array of strings (schema enums)
 
-      * @param  {string[]} enums Array of valid rule options (e.g. ["always", "never"])
 
-      * @returns {void}
 
-      */
 
-     addEnums(enums) {
 
-         this.ruleConfigs = this.ruleConfigs.concat(combineArrays(this.ruleConfigs, enums));
 
-     }
 
-     /**
 
-      * Add rule configurations from a schema object
 
-      * @param  {Object} obj Schema item with type === "object"
 
-      * @returns {boolean} true if at least one schema for the object could be generated, false otherwise
 
-      */
 
-     addObject(obj) {
 
-         const objectConfigSet = {
 
-             objectConfigs: [],
 
-             add(property, values) {
 
-                 for (let idx = 0; idx < values.length; idx++) {
 
-                     const optionObj = {};
 
-                     optionObj[property] = values[idx];
 
-                     this.objectConfigs.push(optionObj);
 
-                 }
 
-             },
 
-             combine() {
 
-                 this.objectConfigs = groupByProperty(this.objectConfigs).reduce((accumulator, objArr) => combinePropertyObjects(accumulator, objArr), []);
 
-             }
 
-         };
 
-         /*
 
-          * The object schema could have multiple independent properties.
 
-          * If any contain enums or booleans, they can be added and then combined
 
-          */
 
-         Object.keys(obj.properties).forEach(prop => {
 
-             if (obj.properties[prop].enum) {
 
-                 objectConfigSet.add(prop, obj.properties[prop].enum);
 
-             }
 
-             if (obj.properties[prop].type && obj.properties[prop].type === "boolean") {
 
-                 objectConfigSet.add(prop, [true, false]);
 
-             }
 
-         });
 
-         objectConfigSet.combine();
 
-         if (objectConfigSet.objectConfigs.length > 0) {
 
-             this.ruleConfigs = this.ruleConfigs.concat(combineArrays(this.ruleConfigs, objectConfigSet.objectConfigs));
 
-             return true;
 
-         }
 
-         return false;
 
-     }
 
- }
 
- /**
 
-  * Generate valid rule configurations based on a schema object
 
-  * @param   {Object} schema  A rule's schema object
 
-  * @returns {array[]}        Valid rule configurations
 
-  */
 
- function generateConfigsFromSchema(schema) {
 
-     const configSet = new RuleConfigSet();
 
-     if (Array.isArray(schema)) {
 
-         for (const opt of schema) {
 
-             if (opt.enum) {
 
-                 configSet.addEnums(opt.enum);
 
-             } else if (opt.type && opt.type === "object") {
 
-                 if (!configSet.addObject(opt)) {
 
-                     break;
 
-                 }
 
-             // TODO (IanVS): support oneOf
 
-             } else {
 
-                 // If we don't know how to fill in this option, don't fill in any of the following options.
 
-                 break;
 
-             }
 
-         }
 
-     }
 
-     configSet.addErrorSeverity();
 
-     return configSet.ruleConfigs;
 
- }
 
- /**
 
-  * Generate possible rule configurations for all of the core rules
 
-  * @returns {rulesConfig} Hash of rule names and arrays of possible configurations
 
-  */
 
- function createCoreRuleConfigs() {
 
-     const ruleList = loadRules();
 
-     return Object.keys(ruleList).reduce((accumulator, id) => {
 
-         const rule = rules.get(id);
 
-         const schema = (typeof rule === "function") ? rule.schema : rule.meta.schema;
 
-         accumulator[id] = generateConfigsFromSchema(schema);
 
-         return accumulator;
 
-     }, {});
 
- }
 
- //------------------------------------------------------------------------------
 
- // Public Interface
 
- //------------------------------------------------------------------------------
 
- module.exports = {
 
-     generateConfigsFromSchema,
 
-     createCoreRuleConfigs
 
- };
 
 
  |