Files
react-native-devtools-frontend/node_modules/eslint/lib/init/autoconfig.js
T
Tim van der Lippe c8f6ffd7c3 Update ESLint to 6.8.0
Also update the PRESUBMIT linter configuration to make sure
it actually runs the linter when updating ESLint. Otherwise,
it would not properly do the full linter check.

R=jacktfranklin@chromium.org
DISABLE_THIRD_PARTY_CHECK=Update ESLint

Bug: 1068145
Change-Id: Ibf2de156366dc64c90ee1aced5537d2038e7a268
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2137384
Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
2020-04-06 13:52:18 +00:00

349 lines
12 KiB
JavaScript

/**
* @fileoverview Used for creating a suggested configuration based on project code.
* @author Ian VanSchooten
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const lodash = require("lodash"),
recConfig = require("../../conf/eslint-recommended"),
ConfigOps = require("../shared/config-ops"),
{ Linter } = require("../linter"),
configRule = require("./config-rule");
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 {
// eslint-disable-next-line jsdoc/require-description
/**
* @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.
* @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
};