Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

config-rule.js 9.3KB

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