summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/config/config-ops.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/eslint/lib/config/config-ops.js')
-rw-r--r--tools/node_modules/eslint/lib/config/config-ops.js383
1 files changed, 383 insertions, 0 deletions
diff --git a/tools/node_modules/eslint/lib/config/config-ops.js b/tools/node_modules/eslint/lib/config/config-ops.js
new file mode 100644
index 0000000000..2ce500a4f4
--- /dev/null
+++ b/tools/node_modules/eslint/lib/config/config-ops.js
@@ -0,0 +1,383 @@
+/**
+ * @fileoverview Config file operations. This file must be usable in the browser,
+ * so no Node-specific code can be here.
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const minimatch = require("minimatch"),
+ path = require("path");
+
+const debug = require("debug")("eslint:config-ops");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+const RULE_SEVERITY_STRINGS = ["off", "warn", "error"],
+ RULE_SEVERITY = RULE_SEVERITY_STRINGS.reduce((map, value, index) => {
+ map[value] = index;
+ return map;
+ }, {}),
+ VALID_SEVERITIES = [0, 1, 2, "off", "warn", "error"];
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+
+ /**
+ * Creates an empty configuration object suitable for merging as a base.
+ * @returns {Object} A configuration object.
+ */
+ createEmptyConfig() {
+ return {
+ globals: {},
+ env: {},
+ rules: {},
+ parserOptions: {}
+ };
+ },
+
+ /**
+ * Creates an environment config based on the specified environments.
+ * @param {Object<string,boolean>} env The environment settings.
+ * @param {Environments} envContext The environment context.
+ * @returns {Object} A configuration object with the appropriate rules and globals
+ * set.
+ */
+ createEnvironmentConfig(env, envContext) {
+
+ const envConfig = this.createEmptyConfig();
+
+ if (env) {
+
+ envConfig.env = env;
+
+ Object.keys(env).filter(name => env[name]).forEach(name => {
+ const environment = envContext.get(name);
+
+ if (environment) {
+ debug(`Creating config for environment ${name}`);
+ if (environment.globals) {
+ Object.assign(envConfig.globals, environment.globals);
+ }
+
+ if (environment.parserOptions) {
+ Object.assign(envConfig.parserOptions, environment.parserOptions);
+ }
+ }
+ });
+ }
+
+ return envConfig;
+ },
+
+ /**
+ * Given a config with environment settings, applies the globals and
+ * ecmaFeatures to the configuration and returns the result.
+ * @param {Object} config The configuration information.
+ * @param {Environments} envContent env context.
+ * @returns {Object} The updated configuration information.
+ */
+ applyEnvironments(config, envContent) {
+ if (config.env && typeof config.env === "object") {
+ debug("Apply environment settings to config");
+ return this.merge(this.createEnvironmentConfig(config.env, envContent), config);
+ }
+
+ return config;
+ },
+
+ /**
+ * Merges two config objects. This will not only add missing keys, but will also modify values to match.
+ * @param {Object} target config object
+ * @param {Object} src config object. Overrides in this config object will take priority over base.
+ * @param {boolean} [combine] Whether to combine arrays or not
+ * @param {boolean} [isRule] Whether its a rule
+ * @returns {Object} merged config object.
+ */
+ merge: function deepmerge(target, src, combine, isRule) {
+
+ /*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2012 Nicholas Fisher
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+ /*
+ * This code is taken from deepmerge repo
+ * (https://github.com/KyleAMathews/deepmerge)
+ * and modified to meet our needs.
+ */
+ const array = Array.isArray(src) || Array.isArray(target);
+ let dst = array && [] || {};
+
+ combine = !!combine;
+ isRule = !!isRule;
+ if (array) {
+ target = target || [];
+
+ // src could be a string, so check for array
+ if (isRule && Array.isArray(src) && src.length > 1) {
+ dst = dst.concat(src);
+ } else {
+ dst = dst.concat(target);
+ }
+ if (typeof src !== "object" && !Array.isArray(src)) {
+ src = [src];
+ }
+ Object.keys(src).forEach((e, i) => {
+ e = src[i];
+ if (typeof dst[i] === "undefined") {
+ dst[i] = e;
+ } else if (typeof e === "object") {
+ if (isRule) {
+ dst[i] = e;
+ } else {
+ dst[i] = deepmerge(target[i], e, combine, isRule);
+ }
+ } else {
+ if (!combine) {
+ dst[i] = e;
+ } else {
+ if (dst.indexOf(e) === -1) {
+ dst.push(e);
+ }
+ }
+ }
+ });
+ } else {
+ if (target && typeof target === "object") {
+ Object.keys(target).forEach(key => {
+ dst[key] = target[key];
+ });
+ }
+ Object.keys(src).forEach(key => {
+ if (key === "overrides") {
+ dst[key] = (target[key] || []).concat(src[key] || []);
+ } else if (Array.isArray(src[key]) || Array.isArray(target[key])) {
+ dst[key] = deepmerge(target[key], src[key], key === "plugins" || key === "extends", isRule);
+ } else if (typeof src[key] !== "object" || !src[key] || key === "exported" || key === "astGlobals") {
+ dst[key] = src[key];
+ } else {
+ dst[key] = deepmerge(target[key] || {}, src[key], combine, key === "rules");
+ }
+ });
+ }
+
+ return dst;
+ },
+
+ /**
+ * Normalizes the severity value of a rule's configuration to a number
+ * @param {(number|string|[number, ...*]|[string, ...*])} ruleConfig A rule's configuration value, generally
+ * received from the user. A valid config value is either 0, 1, 2, the string "off" (treated the same as 0),
+ * the string "warn" (treated the same as 1), the string "error" (treated the same as 2), or an array
+ * whose first element is one of the above values. Strings are matched case-insensitively.
+ * @returns {(0|1|2)} The numeric severity value if the config value was valid, otherwise 0.
+ */
+ getRuleSeverity(ruleConfig) {
+ const severityValue = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
+
+ if (severityValue === 0 || severityValue === 1 || severityValue === 2) {
+ return severityValue;
+ }
+
+ if (typeof severityValue === "string") {
+ return RULE_SEVERITY[severityValue.toLowerCase()] || 0;
+ }
+
+ return 0;
+ },
+
+ /**
+ * Converts old-style severity settings (0, 1, 2) into new-style
+ * severity settings (off, warn, error) for all rules. Assumption is that severity
+ * values have already been validated as correct.
+ * @param {Object} config The config object to normalize.
+ * @returns {void}
+ */
+ normalizeToStrings(config) {
+
+ if (config.rules) {
+ Object.keys(config.rules).forEach(ruleId => {
+ const ruleConfig = config.rules[ruleId];
+
+ if (typeof ruleConfig === "number") {
+ config.rules[ruleId] = RULE_SEVERITY_STRINGS[ruleConfig] || RULE_SEVERITY_STRINGS[0];
+ } else if (Array.isArray(ruleConfig) && typeof ruleConfig[0] === "number") {
+ ruleConfig[0] = RULE_SEVERITY_STRINGS[ruleConfig[0]] || RULE_SEVERITY_STRINGS[0];
+ }
+ });
+ }
+ },
+
+ /**
+ * Determines if the severity for the given rule configuration represents an error.
+ * @param {int|string|Array} ruleConfig The configuration for an individual rule.
+ * @returns {boolean} True if the rule represents an error, false if not.
+ */
+ isErrorSeverity(ruleConfig) {
+
+ let severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
+
+ if (typeof severity === "string") {
+ severity = RULE_SEVERITY[severity.toLowerCase()] || 0;
+ }
+
+ return (typeof severity === "number" && severity === 2);
+ },
+
+ /**
+ * Checks whether a given config has valid severity or not.
+ * @param {number|string|Array} ruleConfig - The configuration for an individual rule.
+ * @returns {boolean} `true` if the configuration has valid severity.
+ */
+ isValidSeverity(ruleConfig) {
+ let severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
+
+ if (typeof severity === "string") {
+ severity = severity.toLowerCase();
+ }
+ return VALID_SEVERITIES.indexOf(severity) !== -1;
+ },
+
+ /**
+ * Checks whether every rule of a given config has valid severity or not.
+ * @param {Object} config - The configuration for rules.
+ * @returns {boolean} `true` if the configuration has valid severity.
+ */
+ isEverySeverityValid(config) {
+ return Object.keys(config).every(ruleId => this.isValidSeverity(config[ruleId]));
+ },
+
+ /**
+ * Merges all configurations in a given config vector. A vector is an array of objects, each containing a config
+ * file path and a list of subconfig indices that match the current file path. All config data is assumed to be
+ * cached.
+ * @param {Array<Object>} vector list of config files and their subconfig indices that match the current file path
+ * @param {Object} configCache the config cache
+ * @returns {Object} config object
+ */
+ getConfigFromVector(vector, configCache) {
+
+ const cachedConfig = configCache.getMergedVectorConfig(vector);
+
+ if (cachedConfig) {
+ return cachedConfig;
+ }
+
+ debug("Using config from partial cache");
+
+ const subvector = Array.from(vector);
+ let nearestCacheIndex = subvector.length - 1,
+ partialCachedConfig;
+
+ while (nearestCacheIndex >= 0) {
+ partialCachedConfig = configCache.getMergedVectorConfig(subvector);
+ if (partialCachedConfig) {
+ break;
+ }
+ subvector.pop();
+ nearestCacheIndex--;
+ }
+
+ if (!partialCachedConfig) {
+ partialCachedConfig = {};
+ }
+
+ let finalConfig = partialCachedConfig;
+
+ // Start from entry immediately following nearest cached config (first uncached entry)
+ for (let i = nearestCacheIndex + 1; i < vector.length; i++) {
+ finalConfig = this.mergeVectorEntry(finalConfig, vector[i], configCache);
+ configCache.setMergedVectorConfig(vector.slice(0, i + 1), finalConfig);
+ }
+
+ return finalConfig;
+ },
+
+ /**
+ * Merges the config options from a single vector entry into the supplied config.
+ * @param {Object} config the base config to merge the vector entry's options into
+ * @param {Object} vectorEntry a single entry from a vector, consisting of a config file path and an array of
+ * matching override indices
+ * @param {Object} configCache the config cache
+ * @returns {Object} merged config object
+ */
+ mergeVectorEntry(config, vectorEntry, configCache) {
+ const vectorEntryConfig = Object.assign({}, configCache.getConfig(vectorEntry.filePath));
+ let mergedConfig = Object.assign({}, config),
+ overrides;
+
+ if (vectorEntryConfig.overrides) {
+ overrides = vectorEntryConfig.overrides.filter(
+ (override, overrideIndex) => vectorEntry.matchingOverrides.indexOf(overrideIndex) !== -1
+ );
+ } else {
+ overrides = [];
+ }
+
+ mergedConfig = this.merge(mergedConfig, vectorEntryConfig);
+
+ delete mergedConfig.overrides;
+
+ mergedConfig = overrides.reduce((lastConfig, override) => this.merge(lastConfig, override), mergedConfig);
+
+ if (mergedConfig.filePath) {
+ delete mergedConfig.filePath;
+ delete mergedConfig.baseDirectory;
+ } else if (mergedConfig.files) {
+ delete mergedConfig.files;
+ }
+
+ return mergedConfig;
+ },
+
+ /**
+ * Checks that the specified file path matches all of the supplied glob patterns.
+ * @param {string} filePath The file path to test patterns against
+ * @param {string|string[]} patterns One or more glob patterns, of which at least one should match the file path
+ * @param {string|string[]} [excludedPatterns] One or more glob patterns, of which none should match the file path
+ * @returns {boolean} True if all the supplied patterns match the file path, false otherwise
+ */
+ pathMatchesGlobs(filePath, patterns, excludedPatterns) {
+ const patternList = [].concat(patterns);
+ const excludedPatternList = [].concat(excludedPatterns || []);
+
+ patternList.concat(excludedPatternList).forEach(pattern => {
+ if (path.isAbsolute(pattern) || pattern.includes("..")) {
+ throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
+ }
+ });
+
+ const opts = { matchBase: true };
+
+ return patternList.some(pattern => minimatch(filePath, pattern, opts)) &&
+ !excludedPatternList.some(excludedPattern => minimatch(filePath, excludedPattern, opts));
+ }
+};