summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/config/autoconfig.js
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2017-12-22 16:53:42 +0100
committerMichaël Zasso <targos@protonmail.com>2018-01-11 09:48:05 +0100
commit3dc30632755713179f345f4af024bd904c6162d0 (patch)
treef28c4f6dd6dfc5992edf301449d1a371d229755b /tools/node_modules/eslint/lib/config/autoconfig.js
parenta2c7085dd4a8e60d1a47572aca8bb6fcb7a32f88 (diff)
downloadandroid-node-v8-3dc30632755713179f345f4af024bd904c6162d0.tar.gz
android-node-v8-3dc30632755713179f345f4af024bd904c6162d0.tar.bz2
android-node-v8-3dc30632755713179f345f4af024bd904c6162d0.zip
tools: move eslint from tools to tools/node_modules
This is required because we need to add the babel-eslint dependency and it has to be able to resolve "eslint". babel-eslint is required to support future ES features such as async iterators and import.meta. Refs: https://github.com/nodejs/node/pull/17755 PR-URL: https://github.com/nodejs/node/pull/17820 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Diffstat (limited to 'tools/node_modules/eslint/lib/config/autoconfig.js')
-rw-r--r--tools/node_modules/eslint/lib/config/autoconfig.js359
1 files changed, 359 insertions, 0 deletions
diff --git a/tools/node_modules/eslint/lib/config/autoconfig.js b/tools/node_modules/eslint/lib/config/autoconfig.js
new file mode 100644
index 0000000000..8536fdc55a
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/autoconfig.js
@@ -0,0 +1,359 @@
+/**
+ * @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
+};