.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / init / config-rule.js
1 /**
2  * @fileoverview Create configurations for a rule
3  * @author Ian VanSchooten
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const builtInRules = require("../rules");
13
14 //------------------------------------------------------------------------------
15 // Helpers
16 //------------------------------------------------------------------------------
17
18 /**
19  * Wrap all of the elements of an array into arrays.
20  * @param   {*[]}     xs Any array.
21  * @returns {Array[]}    An array of arrays.
22  */
23 function explodeArray(xs) {
24     return xs.reduce((accumulator, x) => {
25         accumulator.push([x]);
26         return accumulator;
27     }, []);
28 }
29
30 /**
31  * Mix two arrays such that each element of the second array is concatenated
32  * onto each element of the first array.
33  *
34  * For example:
35  * combineArrays([a, [b, c]], [x, y]); // -> [[a, x], [a, y], [b, c, x], [b, c, y]]
36  * @param   {Array} arr1 The first array to combine.
37  * @param   {Array} arr2 The second array to combine.
38  * @returns {Array}      A mixture of the elements of the first and second arrays.
39  */
40 function combineArrays(arr1, arr2) {
41     const res = [];
42
43     if (arr1.length === 0) {
44         return explodeArray(arr2);
45     }
46     if (arr2.length === 0) {
47         return explodeArray(arr1);
48     }
49     arr1.forEach(x1 => {
50         arr2.forEach(x2 => {
51             res.push([].concat(x1, x2));
52         });
53     });
54     return res;
55 }
56
57 /**
58  * Group together valid rule configurations based on object properties
59  *
60  * e.g.:
61  * groupByProperty([
62  *     {before: true},
63  *     {before: false},
64  *     {after: true},
65  *     {after: false}
66  * ]);
67  *
68  * will return:
69  * [
70  *     [{before: true}, {before: false}],
71  *     [{after: true}, {after: false}]
72  * ]
73  * @param   {Object[]} objects Array of objects, each with one property/value pair
74  * @returns {Array[]}          Array of arrays of objects grouped by property
75  */
76 function groupByProperty(objects) {
77     const groupedObj = objects.reduce((accumulator, obj) => {
78         const prop = Object.keys(obj)[0];
79
80         accumulator[prop] = accumulator[prop] ? accumulator[prop].concat(obj) : [obj];
81         return accumulator;
82     }, {});
83
84     return Object.keys(groupedObj).map(prop => groupedObj[prop]);
85 }
86
87
88 //------------------------------------------------------------------------------
89 // Private
90 //------------------------------------------------------------------------------
91
92 /**
93  * Configuration settings for a rule.
94  *
95  * A configuration can be a single number (severity), or an array where the first
96  * element in the array is the severity, and is the only required element.
97  * Configs may also have one or more additional elements to specify rule
98  * configuration or options.
99  * @typedef {Array|number} ruleConfig
100  * @param {number}  0  The rule's severity (0, 1, 2).
101  */
102
103 /**
104  * Object whose keys are rule names and values are arrays of valid ruleConfig items
105  * which should be linted against the target source code to determine error counts.
106  * (a ruleConfigSet.ruleConfigs).
107  *
108  * e.g. rulesConfig = {
109  *     "comma-dangle": [2, [2, "always"], [2, "always-multiline"], [2, "never"]],
110  *     "no-console": [2]
111  * }
112  * @typedef rulesConfig
113  */
114
115
116 /**
117  * Create valid rule configurations by combining two arrays,
118  * with each array containing multiple objects each with a
119  * single property/value pair and matching properties.
120  *
121  * e.g.:
122  * combinePropertyObjects(
123  *     [{before: true}, {before: false}],
124  *     [{after: true}, {after: false}]
125  * );
126  *
127  * will return:
128  * [
129  *     {before: true, after: true},
130  *     {before: true, after: false},
131  *     {before: false, after: true},
132  *     {before: false, after: false}
133  * ]
134  * @param   {Object[]} objArr1 Single key/value objects, all with the same key
135  * @param   {Object[]} objArr2 Single key/value objects, all with another key
136  * @returns {Object[]}         Combined objects for each combination of input properties and values
137  */
138 function combinePropertyObjects(objArr1, objArr2) {
139     const res = [];
140
141     if (objArr1.length === 0) {
142         return objArr2;
143     }
144     if (objArr2.length === 0) {
145         return objArr1;
146     }
147     objArr1.forEach(obj1 => {
148         objArr2.forEach(obj2 => {
149             const combinedObj = {};
150             const obj1Props = Object.keys(obj1);
151             const obj2Props = Object.keys(obj2);
152
153             obj1Props.forEach(prop1 => {
154                 combinedObj[prop1] = obj1[prop1];
155             });
156             obj2Props.forEach(prop2 => {
157                 combinedObj[prop2] = obj2[prop2];
158             });
159             res.push(combinedObj);
160         });
161     });
162     return res;
163 }
164
165 /**
166  * Creates a new instance of a rule configuration set
167  *
168  * A rule configuration set is an array of configurations that are valid for a
169  * given rule.  For example, the configuration set for the "semi" rule could be:
170  *
171  * ruleConfigSet.ruleConfigs // -> [[2], [2, "always"], [2, "never"]]
172  *
173  * Rule configuration set class
174  */
175 class RuleConfigSet {
176
177     // eslint-disable-next-line jsdoc/require-description
178     /**
179      * @param {ruleConfig[]} configs Valid rule configurations
180      */
181     constructor(configs) {
182
183         /**
184          * Stored valid rule configurations for this instance
185          * @type {Array}
186          */
187         this.ruleConfigs = configs || [];
188     }
189
190     /**
191      * Add a severity level to the front of all configs in the instance.
192      * This should only be called after all configs have been added to the instance.
193      * @returns {void}
194      */
195     addErrorSeverity() {
196         const severity = 2;
197
198         this.ruleConfigs = this.ruleConfigs.map(config => {
199             config.unshift(severity);
200             return config;
201         });
202
203         // Add a single config at the beginning consisting of only the severity
204         this.ruleConfigs.unshift(severity);
205     }
206
207     /**
208      * Add rule configs from an array of strings (schema enums)
209      * @param  {string[]} enums Array of valid rule options (e.g. ["always", "never"])
210      * @returns {void}
211      */
212     addEnums(enums) {
213         this.ruleConfigs = this.ruleConfigs.concat(combineArrays(this.ruleConfigs, enums));
214     }
215
216     /**
217      * Add rule configurations from a schema object
218      * @param  {Object} obj Schema item with type === "object"
219      * @returns {boolean} true if at least one schema for the object could be generated, false otherwise
220      */
221     addObject(obj) {
222         const objectConfigSet = {
223             objectConfigs: [],
224             add(property, values) {
225                 for (let idx = 0; idx < values.length; idx++) {
226                     const optionObj = {};
227
228                     optionObj[property] = values[idx];
229                     this.objectConfigs.push(optionObj);
230                 }
231             },
232
233             combine() {
234                 this.objectConfigs = groupByProperty(this.objectConfigs).reduce((accumulator, objArr) => combinePropertyObjects(accumulator, objArr), []);
235             }
236         };
237
238         /*
239          * The object schema could have multiple independent properties.
240          * If any contain enums or booleans, they can be added and then combined
241          */
242         Object.keys(obj.properties).forEach(prop => {
243             if (obj.properties[prop].enum) {
244                 objectConfigSet.add(prop, obj.properties[prop].enum);
245             }
246             if (obj.properties[prop].type && obj.properties[prop].type === "boolean") {
247                 objectConfigSet.add(prop, [true, false]);
248             }
249         });
250         objectConfigSet.combine();
251
252         if (objectConfigSet.objectConfigs.length > 0) {
253             this.ruleConfigs = this.ruleConfigs.concat(combineArrays(this.ruleConfigs, objectConfigSet.objectConfigs));
254             return true;
255         }
256
257         return false;
258     }
259 }
260
261 /**
262  * Generate valid rule configurations based on a schema object
263  * @param   {Object} schema  A rule's schema object
264  * @returns {Array[]}        Valid rule configurations
265  */
266 function generateConfigsFromSchema(schema) {
267     const configSet = new RuleConfigSet();
268
269     if (Array.isArray(schema)) {
270         for (const opt of schema) {
271             if (opt.enum) {
272                 configSet.addEnums(opt.enum);
273             } else if (opt.type && opt.type === "object") {
274                 if (!configSet.addObject(opt)) {
275                     break;
276                 }
277
278             // TODO (IanVS): support oneOf
279             } else {
280
281                 // If we don't know how to fill in this option, don't fill in any of the following options.
282                 break;
283             }
284         }
285     }
286     configSet.addErrorSeverity();
287     return configSet.ruleConfigs;
288 }
289
290 /**
291  * Generate possible rule configurations for all of the core rules
292  * @param {boolean} noDeprecated Indicates whether ignores deprecated rules or not.
293  * @returns {rulesConfig} Hash of rule names and arrays of possible configurations
294  */
295 function createCoreRuleConfigs(noDeprecated = false) {
296     return Array.from(builtInRules).reduce((accumulator, [id, rule]) => {
297         const schema = (typeof rule === "function") ? rule.schema : rule.meta.schema;
298         const isDeprecated = (typeof rule === "function") ? rule.deprecated : rule.meta.deprecated;
299
300         if (noDeprecated && isDeprecated) {
301             return accumulator;
302         }
303
304         accumulator[id] = generateConfigsFromSchema(schema);
305         return accumulator;
306     }, {});
307 }
308
309
310 //------------------------------------------------------------------------------
311 // Public Interface
312 //------------------------------------------------------------------------------
313
314 module.exports = {
315     generateConfigsFromSchema,
316     createCoreRuleConfigs
317 };