| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 | 
							- /**
 
-  * @fileoverview Used for creating a suggested configuration based on project code.
 
-  * @author Ian VanSchooten
 
-  */
 
- "use strict";
 
- //------------------------------------------------------------------------------
 
- // Requirements
 
- //------------------------------------------------------------------------------
 
- const lodash = require("lodash"),
 
-     Linter = require("../linter"),
 
-     configRule = require("./config-rule"),
 
-     ConfigOps = require("./config-ops"),
 
-     recConfig = require("../../conf/eslint-recommended");
 
- const debug = require("debug")("eslint:autoconfig");
 
- const linter = new Linter();
 
- //------------------------------------------------------------------------------
 
- // Data
 
- //------------------------------------------------------------------------------
 
- const MAX_CONFIG_COMBINATIONS = 17, // 16 combinations + 1 for severity only
 
-     RECOMMENDED_CONFIG_NAME = "eslint:recommended";
 
- //------------------------------------------------------------------------------
 
- // Private
 
- //------------------------------------------------------------------------------
 
- /**
 
-  * Information about a rule configuration, in the context of a Registry.
 
-  *
 
-  * @typedef {Object}     registryItem
 
-  * @param   {ruleConfig} config        A valid configuration for the rule
 
-  * @param   {number}     specificity   The number of elements in the ruleConfig array
 
-  * @param   {number}     errorCount    The number of errors encountered when linting with the config
 
-  */
 
- /**
 
-  * This callback is used to measure execution status in a progress bar
 
-  * @callback progressCallback
 
-  * @param {number} The total number of times the callback will be called.
 
-  */
 
- /**
 
-  * Create registryItems for rules
 
-  * @param   {rulesConfig} rulesConfig Hash of rule names and arrays of ruleConfig items
 
-  * @returns {Object}                  registryItems for each rule in provided rulesConfig
 
-  */
 
- function makeRegistryItems(rulesConfig) {
 
-     return Object.keys(rulesConfig).reduce((accumulator, ruleId) => {
 
-         accumulator[ruleId] = rulesConfig[ruleId].map(config => ({
 
-             config,
 
-             specificity: config.length || 1,
 
-             errorCount: void 0
 
-         }));
 
-         return accumulator;
 
-     }, {});
 
- }
 
- /**
 
-  * Creates an object in which to store rule configs and error counts
 
-  *
 
-  * Unless a rulesConfig is provided at construction, the registry will not contain
 
-  * any rules, only methods.  This will be useful for building up registries manually.
 
-  *
 
-  * Registry class
 
-  */
 
- class Registry {
 
-     /**
 
-      * @param {rulesConfig} [rulesConfig] Hash of rule names and arrays of possible configurations
 
-      */
 
-     constructor(rulesConfig) {
 
-         this.rules = (rulesConfig) ? makeRegistryItems(rulesConfig) : {};
 
-     }
 
-     /**
 
-      * Populate the registry with core rule configs.
 
-      *
 
-      * It will set the registry's `rule` property to an object having rule names
 
-      * as keys and an array of registryItems as values.
 
-      *
 
-      * @returns {void}
 
-      */
 
-     populateFromCoreRules() {
 
-         const rulesConfig = configRule.createCoreRuleConfigs();
 
-         this.rules = makeRegistryItems(rulesConfig);
 
-     }
 
-     /**
 
-      * Creates sets of rule configurations which can be used for linting
 
-      * and initializes registry errors to zero for those configurations (side effect).
 
-      *
 
-      * This combines as many rules together as possible, such that the first sets
 
-      * in the array will have the highest number of rules configured, and later sets
 
-      * will have fewer and fewer, as not all rules have the same number of possible
 
-      * configurations.
 
-      *
 
-      * The length of the returned array will be <= MAX_CONFIG_COMBINATIONS.
 
-      *
 
-      * @param   {Object}   registry The autoconfig registry
 
-      * @returns {Object[]}          "rules" configurations to use for linting
 
-      */
 
-     buildRuleSets() {
 
-         let idx = 0;
 
-         const ruleIds = Object.keys(this.rules),
 
-             ruleSets = [];
 
-         /**
 
-          * Add a rule configuration from the registry to the ruleSets
 
-          *
 
-          * This is broken out into its own function so that it doesn't need to be
 
-          * created inside of the while loop.
 
-          *
 
-          * @param   {string} rule The ruleId to add.
 
-          * @returns {void}
 
-          */
 
-         const addRuleToRuleSet = function(rule) {
 
-             /*
 
-              * This check ensures that there is a rule configuration and that
 
-              * it has fewer than the max combinations allowed.
 
-              * If it has too many configs, we will only use the most basic of
 
-              * the possible configurations.
 
-              */
 
-             const hasFewCombos = (this.rules[rule].length <= MAX_CONFIG_COMBINATIONS);
 
-             if (this.rules[rule][idx] && (hasFewCombos || this.rules[rule][idx].specificity <= 2)) {
 
-                 /*
 
-                  * If the rule has too many possible combinations, only take
 
-                  * simple ones, avoiding objects.
 
-                  */
 
-                 if (!hasFewCombos && typeof this.rules[rule][idx].config[1] === "object") {
 
-                     return;
 
-                 }
 
-                 ruleSets[idx] = ruleSets[idx] || {};
 
-                 ruleSets[idx][rule] = this.rules[rule][idx].config;
 
-                 /*
 
-                  * Initialize errorCount to zero, since this is a config which
 
-                  * will be linted.
 
-                  */
 
-                 this.rules[rule][idx].errorCount = 0;
 
-             }
 
-         }.bind(this);
 
-         while (ruleSets.length === idx) {
 
-             ruleIds.forEach(addRuleToRuleSet);
 
-             idx += 1;
 
-         }
 
-         return ruleSets;
 
-     }
 
-     /**
 
-      * Remove all items from the registry with a non-zero number of errors
 
-      *
 
-      * Note: this also removes rule configurations which were not linted
 
-      * (meaning, they have an undefined errorCount).
 
-      *
 
-      * @returns {void}
 
-      */
 
-     stripFailingConfigs() {
 
-         const ruleIds = Object.keys(this.rules),
 
-             newRegistry = new Registry();
 
-         newRegistry.rules = Object.assign({}, this.rules);
 
-         ruleIds.forEach(ruleId => {
 
-             const errorFreeItems = newRegistry.rules[ruleId].filter(registryItem => (registryItem.errorCount === 0));
 
-             if (errorFreeItems.length > 0) {
 
-                 newRegistry.rules[ruleId] = errorFreeItems;
 
-             } else {
 
-                 delete newRegistry.rules[ruleId];
 
-             }
 
-         });
 
-         return newRegistry;
 
-     }
 
-     /**
 
-      * Removes rule configurations which were not included in a ruleSet
 
-      *
 
-      * @returns {void}
 
-      */
 
-     stripExtraConfigs() {
 
-         const ruleIds = Object.keys(this.rules),
 
-             newRegistry = new Registry();
 
-         newRegistry.rules = Object.assign({}, this.rules);
 
-         ruleIds.forEach(ruleId => {
 
-             newRegistry.rules[ruleId] = newRegistry.rules[ruleId].filter(registryItem => (typeof registryItem.errorCount !== "undefined"));
 
-         });
 
-         return newRegistry;
 
-     }
 
-     /**
 
-      * Creates a registry of rules which had no error-free configs.
 
-      * The new registry is intended to be analyzed to determine whether its rules
 
-      * should be disabled or set to warning.
 
-      *
 
-      * @returns {Registry}  A registry of failing rules.
 
-      */
 
-     getFailingRulesRegistry() {
 
-         const ruleIds = Object.keys(this.rules),
 
-             failingRegistry = new Registry();
 
-         ruleIds.forEach(ruleId => {
 
-             const failingConfigs = this.rules[ruleId].filter(registryItem => (registryItem.errorCount > 0));
 
-             if (failingConfigs && failingConfigs.length === this.rules[ruleId].length) {
 
-                 failingRegistry.rules[ruleId] = failingConfigs;
 
-             }
 
-         });
 
-         return failingRegistry;
 
-     }
 
-     /**
 
-      * Create an eslint config for any rules which only have one configuration
 
-      * in the registry.
 
-      *
 
-      * @returns {Object} An eslint config with rules section populated
 
-      */
 
-     createConfig() {
 
-         const ruleIds = Object.keys(this.rules),
 
-             config = { rules: {} };
 
-         ruleIds.forEach(ruleId => {
 
-             if (this.rules[ruleId].length === 1) {
 
-                 config.rules[ruleId] = this.rules[ruleId][0].config;
 
-             }
 
-         });
 
-         return config;
 
-     }
 
-     /**
 
-      * Return a cloned registry containing only configs with a desired specificity
 
-      *
 
-      * @param   {number} specificity Only keep configs with this specificity
 
-      * @returns {Registry}           A registry of rules
 
-      */
 
-     filterBySpecificity(specificity) {
 
-         const ruleIds = Object.keys(this.rules),
 
-             newRegistry = new Registry();
 
-         newRegistry.rules = Object.assign({}, this.rules);
 
-         ruleIds.forEach(ruleId => {
 
-             newRegistry.rules[ruleId] = this.rules[ruleId].filter(registryItem => (registryItem.specificity === specificity));
 
-         });
 
-         return newRegistry;
 
-     }
 
-     /**
 
-      * Lint SourceCodes against all configurations in the registry, and record results
 
-      *
 
-      * @param   {Object[]} sourceCodes  SourceCode objects for each filename
 
-      * @param   {Object}   config       ESLint config object
 
-      * @param   {progressCallback} [cb] Optional callback for reporting execution status
 
-      * @returns {Registry}              New registry with errorCount populated
 
-      */
 
-     lintSourceCode(sourceCodes, config, cb) {
 
-         let lintedRegistry = new Registry();
 
-         lintedRegistry.rules = Object.assign({}, this.rules);
 
-         const ruleSets = lintedRegistry.buildRuleSets();
 
-         lintedRegistry = lintedRegistry.stripExtraConfigs();
 
-         debug("Linting with all possible rule combinations");
 
-         const filenames = Object.keys(sourceCodes);
 
-         const totalFilesLinting = filenames.length * ruleSets.length;
 
-         filenames.forEach(filename => {
 
-             debug(`Linting file: ${filename}`);
 
-             let ruleSetIdx = 0;
 
-             ruleSets.forEach(ruleSet => {
 
-                 const lintConfig = Object.assign({}, config, { rules: ruleSet });
 
-                 const lintResults = linter.verify(sourceCodes[filename], lintConfig);
 
-                 lintResults.forEach(result => {
 
-                     /*
 
-                      * It is possible that the error is from a configuration comment
 
-                      * in a linted file, in which case there may not be a config
 
-                      * set in this ruleSetIdx.
 
-                      * (https://github.com/eslint/eslint/issues/5992)
 
-                      * (https://github.com/eslint/eslint/issues/7860)
 
-                      */
 
-                     if (
 
-                         lintedRegistry.rules[result.ruleId] &&
 
-                         lintedRegistry.rules[result.ruleId][ruleSetIdx]
 
-                     ) {
 
-                         lintedRegistry.rules[result.ruleId][ruleSetIdx].errorCount += 1;
 
-                     }
 
-                 });
 
-                 ruleSetIdx += 1;
 
-                 if (cb) {
 
-                     cb(totalFilesLinting); // eslint-disable-line callback-return
 
-                 }
 
-             });
 
-             // Deallocate for GC
 
-             sourceCodes[filename] = null;
 
-         });
 
-         return lintedRegistry;
 
-     }
 
- }
 
- /**
 
-  * Extract rule configuration into eslint:recommended where possible.
 
-  *
 
-  * This will return a new config with `"extends": "eslint:recommended"` and
 
-  * only the rules which have configurations different from the recommended config.
 
-  *
 
-  * @param   {Object} config config object
 
-  * @returns {Object}        config object using `"extends": "eslint:recommended"`
 
-  */
 
- function extendFromRecommended(config) {
 
-     const newConfig = Object.assign({}, config);
 
-     ConfigOps.normalizeToStrings(newConfig);
 
-     const recRules = Object.keys(recConfig.rules).filter(ruleId => ConfigOps.isErrorSeverity(recConfig.rules[ruleId]));
 
-     recRules.forEach(ruleId => {
 
-         if (lodash.isEqual(recConfig.rules[ruleId], newConfig.rules[ruleId])) {
 
-             delete newConfig.rules[ruleId];
 
-         }
 
-     });
 
-     newConfig.extends = RECOMMENDED_CONFIG_NAME;
 
-     return newConfig;
 
- }
 
- //------------------------------------------------------------------------------
 
- // Public Interface
 
- //------------------------------------------------------------------------------
 
- module.exports = {
 
-     Registry,
 
-     extendFromRecommended
 
- };
 
 
  |