diff --git a/README.md b/README.md
index e8b005d7de39..bd744d9b4512 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ $ npm install eslint --save-dev
You should then set up a configuration file:
```sh
-$ ./node_modules/.bin/eslint --init
+$ npm init @eslint/config
```
After that, you can run ESLint on any file or directory like this:
@@ -65,7 +65,7 @@ $ ./node_modules/.bin/eslint yourfile.js
## Configuration
-After running `eslint --init`, you'll have a `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
+After running `npm init @eslint/config`, you'll have a `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
```json
{
diff --git a/bin/eslint.js b/bin/eslint.js
index 6b05356b9dbf..d00a870c089c 100755
--- a/bin/eslint.js
+++ b/bin/eslint.js
@@ -124,7 +124,13 @@ ${message}`);
// Call the config initializer if `--init` is present.
if (process.argv.includes("--init")) {
- await require("../lib/init/config-initializer").initializeConfig();
+
+ // `eslint --init` has been moved to `@eslint/create-config`
+ console.warn("You can also run this command directly using 'npm init @eslint/config'.");
+
+ const spawn = require("cross-spawn");
+
+ spawn.sync("npm", ["init", "@eslint/config"], { encoding: "utf8", stdio: "inherit" });
return;
}
diff --git a/docs/user-guide/getting-started.md b/docs/user-guide/getting-started.md
index cf808ddc022c..e8353192b7e8 100644
--- a/docs/user-guide/getting-started.md
+++ b/docs/user-guide/getting-started.md
@@ -23,14 +23,14 @@ yarn add eslint --dev
You should then set up a configuration file, and the easiest way to do that is to use the `--init` flag:
```sh
-$ npx eslint --init
+$ npm init @eslint/config
# or
-$ yarn run eslint --init
+$ yarn create @eslint/config
```
-**Note:** `--init` assumes you have a `package.json` file already. If you don't, make sure to run `npm init` or `yarn init` beforehand.
+**Note:** `npm init @eslint/config` assumes you have a `package.json` file already. If you don't, make sure to run `npm init` or `yarn init` beforehand.
After that, you can run ESLint on any file or directory like this:
@@ -48,7 +48,7 @@ It is also possible to install ESLint globally rather than locally (using `npm i
**Note:** If you are coming from a version before 1.0.0 please see the [migration guide](migrating-to-1.0.0.md).
-After running `eslint --init`, you'll have a `.eslintrc.{js,yml,json}` file in your directory. In it, you'll see some rules configured like this:
+After running `npm init @eslint/config`, you'll have a `.eslintrc.{js,yml,json}` file in your directory. In it, you'll see some rules configured like this:
```json
{
diff --git a/lib/init/autoconfig.js b/lib/init/autoconfig.js
deleted file mode 100644
index ea2523421c2e..000000000000
--- a/lib/init/autoconfig.js
+++ /dev/null
@@ -1,351 +0,0 @@
-/**
- * @fileoverview Used for creating a suggested configuration based on project code.
- * @author Ian VanSchooten
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const equal = require("fast-deep-equal"),
- recConfig = require("../../conf/eslint-recommended"),
- {
- Legacy: {
- ConfigOps
- }
- } = require("@eslint/eslintrc"),
- { 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
- * @property {ruleConfig} config A valid configuration for the rule
- * @property {number} specificity The number of elements in the ruleConfig array
- * @property {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(/* noDeprecated = */ true);
-
- 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 node/callback-return -- End of function
- }
- });
-
- // 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 (equal(recConfig.rules[ruleId], newConfig.rules[ruleId])) {
- delete newConfig.rules[ruleId];
- }
- });
- newConfig.extends.unshift(RECOMMENDED_CONFIG_NAME);
- return newConfig;
-}
-
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-module.exports = {
- Registry,
- extendFromRecommended
-};
diff --git a/lib/init/config-file.js b/lib/init/config-file.js
deleted file mode 100644
index 9eb10fab3a40..000000000000
--- a/lib/init/config-file.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * @fileoverview Helper to locate and load configuration files.
- * @author Nicholas C. Zakas
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const fs = require("fs"),
- path = require("path"),
- stringify = require("json-stable-stringify-without-jsonify");
-
-const debug = require("debug")("eslint:config-file");
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Determines sort order for object keys for json-stable-stringify
- *
- * see: https://github.com/samn/json-stable-stringify#cmp
- * @param {Object} a The first comparison object ({key: akey, value: avalue})
- * @param {Object} b The second comparison object ({key: bkey, value: bvalue})
- * @returns {number} 1 or -1, used in stringify cmp method
- */
-function sortByKey(a, b) {
- return a.key > b.key ? 1 : -1;
-}
-
-//------------------------------------------------------------------------------
-// Private
-//------------------------------------------------------------------------------
-
-/**
- * Writes a configuration file in JSON format.
- * @param {Object} config The configuration object to write.
- * @param {string} filePath The filename to write to.
- * @returns {void}
- * @private
- */
-function writeJSONConfigFile(config, filePath) {
- debug(`Writing JSON config file: ${filePath}`);
-
- const content = `${stringify(config, { cmp: sortByKey, space: 4 })}\n`;
-
- fs.writeFileSync(filePath, content, "utf8");
-}
-
-/**
- * Writes a configuration file in YAML format.
- * @param {Object} config The configuration object to write.
- * @param {string} filePath The filename to write to.
- * @returns {void}
- * @private
- */
-function writeYAMLConfigFile(config, filePath) {
- debug(`Writing YAML config file: ${filePath}`);
-
- // lazy load YAML to improve performance when not used
- const yaml = require("js-yaml");
-
- const content = yaml.dump(config, { sortKeys: true });
-
- fs.writeFileSync(filePath, content, "utf8");
-}
-
-/**
- * Writes a configuration file in JavaScript format.
- * @param {Object} config The configuration object to write.
- * @param {string} filePath The filename to write to.
- * @throws {Error} If an error occurs linting the config file contents.
- * @returns {void}
- * @private
- */
-function writeJSConfigFile(config, filePath) {
- debug(`Writing JS config file: ${filePath}`);
-
- let contentToWrite;
- const stringifiedContent = `module.exports = ${stringify(config, { cmp: sortByKey, space: 4 })};\n`;
-
- try {
- const { CLIEngine } = require("../cli-engine");
- const linter = new CLIEngine({
- baseConfig: config,
- fix: true,
- useEslintrc: false
- });
- const report = linter.executeOnText(stringifiedContent);
-
- contentToWrite = report.results[0].output || stringifiedContent;
- } catch (e) {
- debug("Error linting JavaScript config file, writing unlinted version");
- const errorMessage = e.message;
-
- contentToWrite = stringifiedContent;
- e.message = "An error occurred while generating your JavaScript config file. ";
- e.message += "A config file was still generated, but the config file itself may not follow your linting rules.";
- e.message += `\nError: ${errorMessage}`;
- throw e;
- } finally {
- fs.writeFileSync(filePath, contentToWrite, "utf8");
- }
-}
-
-/**
- * Writes a configuration file.
- * @param {Object} config The configuration object to write.
- * @param {string} filePath The filename to write to.
- * @returns {void}
- * @throws {Error} When an unknown file type is specified.
- * @private
- */
-function write(config, filePath) {
- switch (path.extname(filePath)) {
- case ".js":
- case ".cjs":
- writeJSConfigFile(config, filePath);
- break;
-
- case ".json":
- writeJSONConfigFile(config, filePath);
- break;
-
- case ".yaml":
- case ".yml":
- writeYAMLConfigFile(config, filePath);
- break;
-
- default:
- throw new Error("Can't write to unknown file type.");
- }
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-module.exports = {
- write
-};
diff --git a/lib/init/config-initializer.js b/lib/init/config-initializer.js
deleted file mode 100644
index 3c244b7bcc06..000000000000
--- a/lib/init/config-initializer.js
+++ /dev/null
@@ -1,709 +0,0 @@
-/**
- * @fileoverview Config initialization wizard.
- * @author Ilya Volodin
- */
-
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const util = require("util"),
- path = require("path"),
- fs = require("fs"),
- enquirer = require("enquirer"),
- ProgressBar = require("progress"),
- semver = require("semver"),
- espree = require("espree"),
- recConfig = require("../../conf/eslint-recommended"),
- {
- Legacy: {
- ConfigOps,
- naming
- }
- } = require("@eslint/eslintrc"),
- log = require("../shared/logging"),
- ModuleResolver = require("../shared/relative-module-resolver"),
- autoconfig = require("./autoconfig.js"),
- ConfigFile = require("./config-file"),
- npmUtils = require("./npm-utils"),
- { getSourceCodeOfFiles } = require("./source-code-utils");
-
-const debug = require("debug")("eslint:config-initializer");
-
-//------------------------------------------------------------------------------
-// Private
-//------------------------------------------------------------------------------
-
-/* istanbul ignore next: hard to test fs function */
-/**
- * Create .eslintrc file in the current working directory
- * @param {Object} config object that contains user's answers
- * @param {string} format The file format to write to.
- * @returns {void}
- */
-function writeFile(config, format) {
-
- // default is .js
- let extname = ".js";
-
- if (format === "YAML") {
- extname = ".yml";
- } else if (format === "JSON") {
- extname = ".json";
- } else if (format === "JavaScript") {
- const pkgJSONPath = npmUtils.findPackageJson();
-
- if (pkgJSONPath) {
- const pkgJSONContents = JSON.parse(fs.readFileSync(pkgJSONPath, "utf8"));
-
- if (pkgJSONContents.type === "module") {
- extname = ".cjs";
- }
- }
- }
-
- const installedESLint = config.installedESLint;
-
- delete config.installedESLint;
-
- ConfigFile.write(config, `./.eslintrc${extname}`);
- log.info(`Successfully created .eslintrc${extname} file in ${process.cwd()}`);
-
- if (installedESLint) {
- log.info("ESLint was installed locally. We recommend using this local copy instead of your globally-installed copy.");
- }
-}
-
-/**
- * Get the peer dependencies of the given module.
- * This adds the gotten value to cache at the first time, then reuses it.
- * In a process, this function is called twice, but `npmUtils.fetchPeerDependencies` needs to access network which is relatively slow.
- * @param {string} moduleName The module name to get.
- * @returns {Object} The peer dependencies of the given module.
- * This object is the object of `peerDependencies` field of `package.json`.
- * Returns null if npm was not found.
- */
-function getPeerDependencies(moduleName) {
- let result = getPeerDependencies.cache.get(moduleName);
-
- if (!result) {
- log.info(`Checking peerDependencies of ${moduleName}`);
-
- result = npmUtils.fetchPeerDependencies(moduleName);
- getPeerDependencies.cache.set(moduleName, result);
- }
-
- return result;
-}
-getPeerDependencies.cache = new Map();
-
-/**
- * Return necessary plugins, configs, parsers, etc. based on the config
- * @param {Object} config config object
- * @param {boolean} [installESLint=true] If `false` is given, it does not install eslint.
- * @returns {string[]} An array of modules to be installed.
- */
-function getModulesList(config, installESLint) {
- const modules = {};
-
- // Create a list of modules which should be installed based on config
- if (config.plugins) {
- for (const plugin of config.plugins) {
- const moduleName = naming.normalizePackageName(plugin, "eslint-plugin");
-
- modules[moduleName] = "latest";
- }
- }
- if (config.extends) {
- const extendList = Array.isArray(config.extends) ? config.extends : [config.extends];
-
- for (const extend of extendList) {
- if (extend.startsWith("eslint:") || extend.startsWith("plugin:")) {
- continue;
- }
- const moduleName = naming.normalizePackageName(extend, "eslint-config");
-
- modules[moduleName] = "latest";
- Object.assign(
- modules,
- getPeerDependencies(`${moduleName}@latest`)
- );
- }
- }
-
- const parser = config.parser || (config.parserOptions && config.parserOptions.parser);
-
- if (parser) {
- modules[parser] = "latest";
- }
-
- if (installESLint === false) {
- delete modules.eslint;
- } else {
- const installStatus = npmUtils.checkDevDeps(["eslint"]);
-
- // Mark to show messages if it's new installation of eslint.
- if (installStatus.eslint === false) {
- log.info("Local ESLint installation not found.");
- modules.eslint = modules.eslint || "latest";
- config.installedESLint = true;
- }
- }
-
- return Object.keys(modules).map(name => `${name}@${modules[name]}`);
-}
-
-/**
- * Set the `rules` of a config by examining a user's source code
- *
- * Note: This clones the config object and returns a new config to avoid mutating
- * the original config parameter.
- * @param {Object} answers answers received from enquirer
- * @param {Object} config config object
- * @throws {Error} If source code retrieval fails or source code file count is 0.
- * @returns {Object} config object with configured rules
- */
-function configureRules(answers, config) {
- const BAR_TOTAL = 20,
- BAR_SOURCE_CODE_TOTAL = 4,
- newConfig = Object.assign({}, config),
- disabledConfigs = {};
- let sourceCodes,
- registry;
-
- // Set up a progress bar, as this process can take a long time
- const bar = new ProgressBar("Determining Config: :percent [:bar] :elapseds elapsed, eta :etas ", {
- width: 30,
- total: BAR_TOTAL
- });
-
- bar.tick(0); // Shows the progress bar
-
- // Get the SourceCode of all chosen files
- const patterns = answers.patterns.split(/[\s]+/u);
-
- try {
- sourceCodes = getSourceCodeOfFiles(patterns, { baseConfig: newConfig, useEslintrc: false }, total => {
- bar.tick((BAR_SOURCE_CODE_TOTAL / total));
- });
- } catch (e) {
- log.info("\n");
- throw e;
- }
- const fileQty = Object.keys(sourceCodes).length;
-
- if (fileQty === 0) {
- log.info("\n");
- throw new Error("Automatic Configuration failed. No files were able to be parsed.");
- }
-
- // Create a registry of rule configs
- registry = new autoconfig.Registry();
- registry.populateFromCoreRules();
-
- // Lint all files with each rule config in the registry
- registry = registry.lintSourceCode(sourceCodes, newConfig, total => {
- bar.tick((BAR_TOTAL - BAR_SOURCE_CODE_TOTAL) / total); // Subtract out ticks used at beginning
- });
- debug(`\nRegistry: ${util.inspect(registry.rules, { depth: null })}`);
-
- // Create a list of recommended rules, because we don't want to disable them
- const recRules = Object.keys(recConfig.rules).filter(ruleId => ConfigOps.isErrorSeverity(recConfig.rules[ruleId]));
-
- // Find and disable rules which had no error-free configuration
- const failingRegistry = registry.getFailingRulesRegistry();
-
- Object.keys(failingRegistry.rules).forEach(ruleId => {
-
- // If the rule is recommended, set it to error, otherwise disable it
- disabledConfigs[ruleId] = (recRules.indexOf(ruleId) !== -1) ? 2 : 0;
- });
-
- // Now that we know which rules to disable, strip out configs with errors
- registry = registry.stripFailingConfigs();
-
- /*
- * If there is only one config that results in no errors for a rule, we should use it.
- * createConfig will only add rules that have one configuration in the registry.
- */
- const singleConfigs = registry.createConfig().rules;
-
- /*
- * The "sweet spot" for number of options in a config seems to be two (severity plus one option).
- * Very often, a third option (usually an object) is available to address
- * edge cases, exceptions, or unique situations. We will prefer to use a config with
- * specificity of two.
- */
- const specTwoConfigs = registry.filterBySpecificity(2).createConfig().rules;
-
- // Maybe a specific combination using all three options works
- const specThreeConfigs = registry.filterBySpecificity(3).createConfig().rules;
-
- // If all else fails, try to use the default (severity only)
- const defaultConfigs = registry.filterBySpecificity(1).createConfig().rules;
-
- // Combine configs in reverse priority order (later take precedence)
- newConfig.rules = Object.assign({}, disabledConfigs, defaultConfigs, specThreeConfigs, specTwoConfigs, singleConfigs);
-
- // Make sure progress bar has finished (floating point rounding)
- bar.update(BAR_TOTAL);
-
- // Log out some stats to let the user know what happened
- const finalRuleIds = Object.keys(newConfig.rules);
- const totalRules = finalRuleIds.length;
- const enabledRules = finalRuleIds.filter(ruleId => (newConfig.rules[ruleId] !== 0)).length;
- const resultMessage = [
- `\nEnabled ${enabledRules} out of ${totalRules}`,
- `rules based on ${fileQty}`,
- `file${(fileQty === 1) ? "." : "s."}`
- ].join(" ");
-
- log.info(resultMessage);
-
- ConfigOps.normalizeToStrings(newConfig);
- return newConfig;
-}
-
-/**
- * process user's answers and create config object
- * @param {Object} answers answers received from enquirer
- * @returns {Object} config object
- */
-function processAnswers(answers) {
- let config = {
- rules: {},
- env: {},
- parserOptions: {},
- extends: []
- };
-
- config.parserOptions.ecmaVersion = espree.latestEcmaVersion;
- config.env.es2021 = true;
-
- // set the module type
- if (answers.moduleType === "esm") {
- config.parserOptions.sourceType = "module";
- } else if (answers.moduleType === "commonjs") {
- config.env.commonjs = true;
- }
-
- // add in browser and node environments if necessary
- answers.env.forEach(env => {
- config.env[env] = true;
- });
-
- // add in library information
- if (answers.framework === "react") {
- config.parserOptions.ecmaFeatures = {
- jsx: true
- };
- config.plugins = ["react"];
- config.extends.push("plugin:react/recommended");
- } else if (answers.framework === "vue") {
- config.plugins = ["vue"];
- config.extends.push("plugin:vue/essential");
- }
-
- if (answers.typescript) {
- if (answers.framework === "vue") {
- config.parserOptions.parser = "@typescript-eslint/parser";
- } else {
- config.parser = "@typescript-eslint/parser";
- }
-
- if (Array.isArray(config.plugins)) {
- config.plugins.push("@typescript-eslint");
- } else {
- config.plugins = ["@typescript-eslint"];
- }
- }
-
- // setup rules based on problems/style enforcement preferences
- if (answers.purpose === "problems") {
- config.extends.unshift("eslint:recommended");
- } else if (answers.purpose === "style") {
- if (answers.source === "prompt") {
- config.extends.unshift("eslint:recommended");
- config.rules.indent = ["error", answers.indent];
- config.rules.quotes = ["error", answers.quotes];
- config.rules["linebreak-style"] = ["error", answers.linebreak];
- config.rules.semi = ["error", answers.semi ? "always" : "never"];
- } else if (answers.source === "auto") {
- config = configureRules(answers, config);
- config = autoconfig.extendFromRecommended(config);
- }
- }
- if (answers.typescript && config.extends.includes("eslint:recommended")) {
- config.extends.push("plugin:@typescript-eslint/recommended");
- }
-
- // normalize extends
- if (config.extends.length === 0) {
- delete config.extends;
- } else if (config.extends.length === 1) {
- config.extends = config.extends[0];
- }
-
- ConfigOps.normalizeToStrings(config);
- return config;
-}
-
-/**
- * Get the version of the local ESLint.
- * @returns {string|null} The version. If the local ESLint was not found, returns null.
- */
-function getLocalESLintVersion() {
- try {
- const eslintPath = ModuleResolver.resolve("eslint", path.join(process.cwd(), "__placeholder__.js"));
- const eslint = require(eslintPath);
-
- return eslint.linter.version || null;
- } catch {
- return null;
- }
-}
-
-/**
- * Get the shareable config name of the chosen style guide.
- * @param {Object} answers The answers object.
- * @returns {string} The shareable config name.
- */
-function getStyleGuideName(answers) {
- if (answers.styleguide === "airbnb" && answers.framework !== "react") {
- return "airbnb-base";
- }
- return answers.styleguide;
-}
-
-/**
- * Check whether the local ESLint version conflicts with the required version of the chosen shareable config.
- * @param {Object} answers The answers object.
- * @returns {boolean} `true` if the local ESLint is found then it conflicts with the required version of the chosen shareable config.
- */
-function hasESLintVersionConflict(answers) {
-
- // Get the local ESLint version.
- const localESLintVersion = getLocalESLintVersion();
-
- if (!localESLintVersion) {
- return false;
- }
-
- // Get the required range of ESLint version.
- const configName = getStyleGuideName(answers);
- const moduleName = `eslint-config-${configName}@latest`;
- const peerDependencies = getPeerDependencies(moduleName) || {};
- const requiredESLintVersionRange = peerDependencies.eslint;
-
- if (!requiredESLintVersionRange) {
- return false;
- }
-
- answers.localESLintVersion = localESLintVersion;
- answers.requiredESLintVersionRange = requiredESLintVersionRange;
-
- // Check the version.
- if (semver.satisfies(localESLintVersion, requiredESLintVersionRange)) {
- answers.installESLint = false;
- return false;
- }
-
- return true;
-}
-
-/**
- * Install modules.
- * @param {string[]} modules Modules to be installed.
- * @returns {void}
- */
-function installModules(modules) {
- log.info(`Installing ${modules.join(", ")}`);
- npmUtils.installSyncSaveDev(modules);
-}
-
-/* istanbul ignore next: no need to test enquirer */
-/**
- * Ask user to install modules.
- * @param {string[]} modules Array of modules to be installed.
- * @param {boolean} packageJsonExists Indicates if package.json is existed.
- * @returns {Promise} Answer that indicates if user wants to install.
- */
-function askInstallModules(modules, packageJsonExists) {
-
- // If no modules, do nothing.
- if (modules.length === 0) {
- return Promise.resolve();
- }
-
- log.info("The config that you've selected requires the following dependencies:\n");
- log.info(modules.join(" "));
- return enquirer.prompt([
- {
- type: "toggle",
- name: "executeInstallation",
- message: "Would you like to install them now with npm?",
- enabled: "Yes",
- disabled: "No",
- initial: 1,
- skip() {
- return !(modules.length && packageJsonExists);
- },
- result(input) {
- return this.skipped ? null : input;
- }
- }
- ]).then(({ executeInstallation }) => {
- if (executeInstallation) {
- installModules(modules);
- }
- });
-}
-
-/* istanbul ignore next: no need to test enquirer */
-/**
- * Ask use a few questions on command prompt
- * @returns {Promise} The promise with the result of the prompt
- */
-function promptUser() {
-
- return enquirer.prompt([
- {
- type: "select",
- name: "purpose",
- message: "How would you like to use ESLint?",
-
- // The returned number matches the name value of nth in the choices array.
- initial: 1,
- choices: [
- { message: "To check syntax only", name: "syntax" },
- { message: "To check syntax and find problems", name: "problems" },
- { message: "To check syntax, find problems, and enforce code style", name: "style" }
- ]
- },
- {
- type: "select",
- name: "moduleType",
- message: "What type of modules does your project use?",
- initial: 0,
- choices: [
- { message: "JavaScript modules (import/export)", name: "esm" },
- { message: "CommonJS (require/exports)", name: "commonjs" },
- { message: "None of these", name: "none" }
- ]
- },
- {
- type: "select",
- name: "framework",
- message: "Which framework does your project use?",
- initial: 0,
- choices: [
- { message: "React", name: "react" },
- { message: "Vue.js", name: "vue" },
- { message: "None of these", name: "none" }
- ]
- },
- {
- type: "toggle",
- name: "typescript",
- message: "Does your project use TypeScript?",
- enabled: "Yes",
- disabled: "No",
- initial: 0
- },
- {
- type: "multiselect",
- name: "env",
- message: "Where does your code run?",
- hint: "(Press to select, to toggle all, to invert selection)",
- initial: 0,
- choices: [
- { message: "Browser", name: "browser" },
- { message: "Node", name: "node" }
- ]
- },
- {
- type: "select",
- name: "source",
- message: "How would you like to define a style for your project?",
- choices: [
- { message: "Use a popular style guide", name: "guide" },
- { message: "Answer questions about your style", name: "prompt" },
- { message: "Inspect your JavaScript file(s)", name: "auto" }
- ],
- skip() {
- return this.state.answers.purpose !== "style";
- },
- result(input) {
- return this.skipped ? null : input;
- }
- },
- {
- type: "select",
- name: "styleguide",
- message: "Which style guide do you want to follow?",
- choices: [
- { message: "Airbnb: https://github.com/airbnb/javascript", name: "airbnb" },
- { message: "Standard: https://github.com/standard/standard", name: "standard" },
- { message: "Google: https://github.com/google/eslint-config-google", name: "google" },
- { message: "XO: https://github.com/xojs/eslint-config-xo", name: "xo" }
- ],
- skip() {
- this.state.answers.packageJsonExists = npmUtils.checkPackageJson();
- return !(this.state.answers.source === "guide" && this.state.answers.packageJsonExists);
- },
- result(input) {
- return this.skipped ? null : input;
- }
- },
- {
- type: "input",
- name: "patterns",
- message: "Which file(s), path(s), or glob(s) should be examined?",
- skip() {
- return this.state.answers.source !== "auto";
- },
- validate(input) {
- if (!this.skipped && input.trim().length === 0 && input.trim() !== ",") {
- return "You must tell us what code to examine. Try again.";
- }
- return true;
- }
- },
- {
- type: "select",
- name: "format",
- message: "What format do you want your config file to be in?",
- initial: 0,
- choices: ["JavaScript", "YAML", "JSON"]
- },
- {
- type: "toggle",
- name: "installESLint",
- message() {
- const { answers } = this.state;
- const verb = semver.ltr(answers.localESLintVersion, answers.requiredESLintVersionRange)
- ? "upgrade"
- : "downgrade";
-
- return `The style guide "${answers.styleguide}" requires eslint@${answers.requiredESLintVersionRange}. You are currently using eslint@${answers.localESLintVersion}.\n Do you want to ${verb}?`;
- },
- enabled: "Yes",
- disabled: "No",
- initial: 1,
- skip() {
- return !(this.state.answers.source === "guide" && this.state.answers.packageJsonExists && hasESLintVersionConflict(this.state.answers));
- },
- result(input) {
- return this.skipped ? null : input;
- }
- }
- ]).then(earlyAnswers => {
-
- // early exit if no style guide is necessary
- if (earlyAnswers.purpose !== "style") {
- const config = processAnswers(earlyAnswers);
- const modules = getModulesList(config);
-
- return askInstallModules(modules, earlyAnswers.packageJsonExists)
- .then(() => writeFile(config, earlyAnswers.format));
- }
-
- // early exit if you are using a style guide
- if (earlyAnswers.source === "guide") {
- if (!earlyAnswers.packageJsonExists) {
- log.info("A package.json is necessary to install plugins such as style guides. Run `npm init` to create a package.json file and try again.");
- return void 0;
- }
- if (earlyAnswers.installESLint === false && !semver.satisfies(earlyAnswers.localESLintVersion, earlyAnswers.requiredESLintVersionRange)) {
- log.info(`Note: it might not work since ESLint's version is mismatched with the ${earlyAnswers.styleguide} config.`);
- }
- if (earlyAnswers.styleguide === "airbnb" && earlyAnswers.framework !== "react") {
- earlyAnswers.styleguide = "airbnb-base";
- }
-
- const config = processAnswers(earlyAnswers);
-
- if (Array.isArray(config.extends)) {
- config.extends.push(earlyAnswers.styleguide);
- } else if (config.extends) {
- config.extends = [config.extends, earlyAnswers.styleguide];
- } else {
- config.extends = [earlyAnswers.styleguide];
- }
-
- const modules = getModulesList(config);
-
- return askInstallModules(modules, earlyAnswers.packageJsonExists)
- .then(() => writeFile(config, earlyAnswers.format));
-
- }
-
- if (earlyAnswers.source === "auto") {
- const combinedAnswers = Object.assign({}, earlyAnswers);
- const config = processAnswers(combinedAnswers);
- const modules = getModulesList(config);
-
- return askInstallModules(modules).then(() => writeFile(config, earlyAnswers.format));
- }
-
- // continue with the style questions otherwise...
- return enquirer.prompt([
- {
- type: "select",
- name: "indent",
- message: "What style of indentation do you use?",
- initial: 0,
- choices: [{ message: "Tabs", name: "tab" }, { message: "Spaces", name: 4 }]
- },
- {
- type: "select",
- name: "quotes",
- message: "What quotes do you use for strings?",
- initial: 0,
- choices: [{ message: "Double", name: "double" }, { message: "Single", name: "single" }]
- },
- {
- type: "select",
- name: "linebreak",
- message: "What line endings do you use?",
- initial: 0,
- choices: [{ message: "Unix", name: "unix" }, { message: "Windows", name: "windows" }]
- },
- {
- type: "toggle",
- name: "semi",
- message: "Do you require semicolons?",
- enabled: "Yes",
- disabled: "No",
- initial: 1
- }
- ]).then(answers => {
- const totalAnswers = Object.assign({}, earlyAnswers, answers);
-
- const config = processAnswers(totalAnswers);
- const modules = getModulesList(config);
-
- return askInstallModules(modules).then(() => writeFile(config, earlyAnswers.format));
- });
- });
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-const init = {
- getModulesList,
- hasESLintVersionConflict,
- installModules,
- processAnswers,
- writeFile,
- /* istanbul ignore next */initializeConfig() {
- return promptUser();
- }
-};
-
-module.exports = init;
diff --git a/lib/init/npm-utils.js b/lib/init/npm-utils.js
deleted file mode 100644
index 4a8efe964f33..000000000000
--- a/lib/init/npm-utils.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
- * @fileoverview Utility for executing npm commands.
- * @author Ian VanSchooten
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const fs = require("fs"),
- spawn = require("cross-spawn"),
- path = require("path"),
- log = require("../shared/logging");
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Find the closest package.json file, starting at process.cwd (by default),
- * and working up to root.
- * @param {string} [startDir=process.cwd()] Starting directory
- * @returns {string} Absolute path to closest package.json file
- */
-function findPackageJson(startDir) {
- let dir = path.resolve(startDir || process.cwd());
-
- do {
- const pkgFile = path.join(dir, "package.json");
-
- if (!fs.existsSync(pkgFile) || !fs.statSync(pkgFile).isFile()) {
- dir = path.join(dir, "..");
- continue;
- }
- return pkgFile;
- } while (dir !== path.resolve(dir, ".."));
- return null;
-}
-
-//------------------------------------------------------------------------------
-// Private
-//------------------------------------------------------------------------------
-
-/**
- * Install node modules synchronously and save to devDependencies in package.json
- * @param {string|string[]} packages Node module or modules to install
- * @returns {void}
- */
-function installSyncSaveDev(packages) {
- const packageList = Array.isArray(packages) ? packages : [packages];
- const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packageList), { stdio: "inherit" });
- const error = npmProcess.error;
-
- if (error && error.code === "ENOENT") {
- const pluralS = packageList.length > 1 ? "s" : "";
-
- log.error(`Could not execute npm. Please install the following package${pluralS} with a package manager of your choice: ${packageList.join(", ")}`);
- }
-}
-
-/**
- * Fetch `peerDependencies` of the given package by `npm show` command.
- * @param {string} packageName The package name to fetch peerDependencies.
- * @returns {Object} Gotten peerDependencies. Returns null if npm was not found.
- */
-function fetchPeerDependencies(packageName) {
- const npmProcess = spawn.sync(
- "npm",
- ["show", "--json", packageName, "peerDependencies"],
- { encoding: "utf8" }
- );
-
- const error = npmProcess.error;
-
- if (error && error.code === "ENOENT") {
- return null;
- }
- const fetchedText = npmProcess.stdout.trim();
-
- return JSON.parse(fetchedText || "{}");
-
-
-}
-
-/**
- * Check whether node modules are include in a project's package.json.
- * @param {string[]} packages Array of node module names
- * @param {Object} opt Options Object
- * @param {boolean} opt.dependencies Set to true to check for direct dependencies
- * @param {boolean} opt.devDependencies Set to true to check for development dependencies
- * @param {boolean} opt.startdir Directory to begin searching from
- * @throws {Error} If cannot find valid `package.json` file.
- * @returns {Object} An object whose keys are the module names
- * and values are booleans indicating installation.
- */
-function check(packages, opt) {
- const deps = new Set();
- const pkgJson = (opt) ? findPackageJson(opt.startDir) : findPackageJson();
- let fileJson;
-
- if (!pkgJson) {
- throw new Error("Could not find a package.json file. Run 'npm init' to create one.");
- }
-
- try {
- fileJson = JSON.parse(fs.readFileSync(pkgJson, "utf8"));
- } catch (e) {
- const error = new Error(e);
-
- error.messageTemplate = "failed-to-read-json";
- error.messageData = {
- path: pkgJson,
- message: e.message
- };
- throw error;
- }
-
- ["dependencies", "devDependencies"].forEach(key => {
- if (opt[key] && typeof fileJson[key] === "object") {
- Object.keys(fileJson[key]).forEach(dep => deps.add(dep));
- }
- });
-
- return packages.reduce((status, pkg) => {
- status[pkg] = deps.has(pkg);
- return status;
- }, {});
-}
-
-/**
- * Check whether node modules are included in the dependencies of a project's
- * package.json.
- *
- * Convenience wrapper around check().
- * @param {string[]} packages Array of node modules to check.
- * @param {string} rootDir The directory containing a package.json
- * @returns {Object} An object whose keys are the module names
- * and values are booleans indicating installation.
- */
-function checkDeps(packages, rootDir) {
- return check(packages, { dependencies: true, startDir: rootDir });
-}
-
-/**
- * Check whether node modules are included in the devDependencies of a project's
- * package.json.
- *
- * Convenience wrapper around check().
- * @param {string[]} packages Array of node modules to check.
- * @returns {Object} An object whose keys are the module names
- * and values are booleans indicating installation.
- */
-function checkDevDeps(packages) {
- return check(packages, { devDependencies: true });
-}
-
-/**
- * Check whether package.json is found in current path.
- * @param {string} [startDir] Starting directory
- * @returns {boolean} Whether a package.json is found in current path.
- */
-function checkPackageJson(startDir) {
- return !!findPackageJson(startDir);
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-module.exports = {
- installSyncSaveDev,
- fetchPeerDependencies,
- findPackageJson,
- checkDeps,
- checkDevDeps,
- checkPackageJson
-};
diff --git a/lib/init/source-code-utils.js b/lib/init/source-code-utils.js
deleted file mode 100644
index 08c20e5d56e2..000000000000
--- a/lib/init/source-code-utils.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * @fileoverview Tools for obtaining SourceCode objects.
- * @author Ian VanSchooten
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const { CLIEngine } = require("../cli-engine");
-
-/*
- * This is used for:
- *
- * 1. Enumerate target file because we have not expose such a API on `CLIEngine`
- * (https://github.com/eslint/eslint/issues/11222).
- * 2. Create `SourceCode` instances. Because we don't have any function which
- * instantiate `SourceCode` so it needs to take the created `SourceCode`
- * instance out after linting.
- *
- * TODO1: Expose the API that enumerates target files.
- * TODO2: Extract the creation logic of `SourceCode` from `Linter` class.
- */
-const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line node/no-restricted-require -- Todo
-
-const debug = require("debug")("eslint:source-code-utils");
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Get the SourceCode object for a single file
- * @param {string} filename The fully resolved filename to get SourceCode from.
- * @param {Object} engine A CLIEngine.
- * @throws {Error} Upon fatal errors from execution.
- * @returns {Array} Array of the SourceCode object representing the file
- * and fatal error message.
- */
-function getSourceCodeOfFile(filename, engine) {
- debug("getting sourceCode of", filename);
- const results = engine.executeOnFiles([filename]);
-
- if (results && results.results[0] && results.results[0].messages[0] && results.results[0].messages[0].fatal) {
- const msg = results.results[0].messages[0];
-
- throw new Error(`(${filename}:${msg.line}:${msg.column}) ${msg.message}`);
- }
-
- // TODO: extract the logic that creates source code objects to `SourceCode#parse(text, options)` or something like.
- const { linter } = getCLIEngineInternalSlots(engine);
- const sourceCode = linter.getSourceCode();
-
- return sourceCode;
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-
-/**
- * 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.
- */
-
-/**
- * Gets the SourceCode of a single file, or set of files.
- * @param {string[]|string} patterns A filename, directory name, or glob, or an array of them
- * @param {Object} options A CLIEngine options object. If not provided, the default cli options will be used.
- * @param {progressCallback} callback Callback for reporting execution status
- * @returns {Object} The SourceCode of all processed files.
- */
-function getSourceCodeOfFiles(patterns, options, callback) {
- const sourceCodes = {};
- const globPatternsList = typeof patterns === "string" ? [patterns] : patterns;
- const engine = new CLIEngine({ ...options, rules: {} });
-
- // TODO: make file iteration as a public API and use it.
- const { fileEnumerator } = getCLIEngineInternalSlots(engine);
- const filenames =
- Array.from(fileEnumerator.iterateFiles(globPatternsList))
- .filter(entry => !entry.ignored)
- .map(entry => entry.filePath);
-
- if (filenames.length === 0) {
- debug(`Did not find any files matching pattern(s): ${globPatternsList}`);
- }
-
- filenames.forEach(filename => {
- const sourceCode = getSourceCodeOfFile(filename, engine);
-
- if (sourceCode) {
- debug("got sourceCode of", filename);
- sourceCodes[filename] = sourceCode;
- }
- if (callback) {
- callback(filenames.length); // eslint-disable-line node/callback-return -- End of function
- }
- });
-
- return sourceCodes;
-}
-
-module.exports = {
- getSourceCodeOfFiles
-};
diff --git a/package.json b/package.json
index fee3bf0f67b2..dfb28d5eaac6 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,6 @@
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
- "enquirer": "^2.3.5",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.1.0",
"eslint-utils": "^3.0.0",
@@ -78,9 +77,7 @@
"minimatch": "^3.0.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
- "progress": "^2.0.0",
"regexpp": "^3.2.0",
- "semver": "^7.2.1",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
"text-table": "^0.2.0",
@@ -124,10 +121,12 @@
"node-polyfill-webpack-plugin": "^1.0.3",
"npm-license": "^0.3.3",
"nyc": "^15.0.1",
+ "progress": "^2.0.3",
"proxyquire": "^2.0.1",
"puppeteer": "^9.1.1",
"recast": "^0.20.4",
"regenerator-runtime": "^0.13.2",
+ "semver": "^7.3.5",
"shelljs": "^0.8.2",
"sinon": "^11.0.0",
"temp": "^0.9.0",
diff --git a/tests/fixtures/config-initializer/lib/doubleQuotes.js b/tests/fixtures/config-initializer/lib/doubleQuotes.js
deleted file mode 100644
index 1cd4a2abbe20..000000000000
--- a/tests/fixtures/config-initializer/lib/doubleQuotes.js
+++ /dev/null
@@ -1 +0,0 @@
-var foo = "doubleQuotes";
diff --git a/tests/fixtures/config-initializer/lib/no-semi.js b/tests/fixtures/config-initializer/lib/no-semi.js
deleted file mode 100644
index bf10b1cfd192..000000000000
--- a/tests/fixtures/config-initializer/lib/no-semi.js
+++ /dev/null
@@ -1 +0,0 @@
-var name = "ESLint"
diff --git a/tests/fixtures/config-initializer/new-es-features/new-es-features.js b/tests/fixtures/config-initializer/new-es-features/new-es-features.js
deleted file mode 100644
index c3e03fc32ca3..000000000000
--- a/tests/fixtures/config-initializer/new-es-features/new-es-features.js
+++ /dev/null
@@ -1,3 +0,0 @@
-async function fn() {
- await Promise.resolve();
-}
diff --git a/tests/fixtures/config-initializer/parse-error/parse-error.js b/tests/fixtures/config-initializer/parse-error/parse-error.js
deleted file mode 100644
index 90bd920ceff5..000000000000
--- a/tests/fixtures/config-initializer/parse-error/parse-error.js
+++ /dev/null
@@ -1 +0,0 @@
-+;
diff --git a/tests/fixtures/config-initializer/singleQuotes.js b/tests/fixtures/config-initializer/singleQuotes.js
deleted file mode 100644
index db1e01feda6b..000000000000
--- a/tests/fixtures/config-initializer/singleQuotes.js
+++ /dev/null
@@ -1 +0,0 @@
-var foo = 'singleQuotes';
diff --git a/tests/fixtures/config-initializer/tests/console-log.js b/tests/fixtures/config-initializer/tests/console-log.js
deleted file mode 100644
index 929e82a146d2..000000000000
--- a/tests/fixtures/config-initializer/tests/console-log.js
+++ /dev/null
@@ -1 +0,0 @@
-console.log("I'm a log");
diff --git a/tests/fixtures/config-initializer/tests/doubleQuotes.js b/tests/fixtures/config-initializer/tests/doubleQuotes.js
deleted file mode 100644
index 1cd4a2abbe20..000000000000
--- a/tests/fixtures/config-initializer/tests/doubleQuotes.js
+++ /dev/null
@@ -1 +0,0 @@
-var foo = "doubleQuotes";
diff --git a/tests/lib/init/autoconfig.js b/tests/lib/init/autoconfig.js
deleted file mode 100644
index 3db6a63645f9..000000000000
--- a/tests/lib/init/autoconfig.js
+++ /dev/null
@@ -1,385 +0,0 @@
-/**
- * @fileoverview Used for creating a suggested configuration based on project code.
- * @author Ian VanSchooten
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const assert = require("chai").assert,
- autoconfig = require("../../../lib/init/autoconfig"),
- sourceCodeUtils = require("../../../lib/init/source-code-utils"),
- baseDefaultOptions = require("../../../conf/default-cli-options"),
- recommendedConfig = require("../../conf/eslint-recommended");
-
-const defaultOptions = Object.assign({}, baseDefaultOptions, { cwd: process.cwd() });
-
-//------------------------------------------------------------------------------
-// Data
-//------------------------------------------------------------------------------
-
-const SOURCE_CODE_FIXTURE_FILENAME = "./tests/fixtures/autoconfig/source.js";
-const CONFIG_COMMENTS_FILENAME = "./tests/fixtures/autoconfig/source-with-comments.js";
-const SEVERITY = 2;
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-const rulesConfig = {
- semi: [SEVERITY, [SEVERITY, "always"], [SEVERITY, "never"]],
- "semi-spacing": [SEVERITY,
- [SEVERITY, { before: true, after: true }],
- [SEVERITY, { before: true, after: false }],
- [SEVERITY, { before: false, after: true }],
- [SEVERITY, { before: false, after: false }]
- ],
- quotes: [SEVERITY,
- [SEVERITY, "single"],
- [SEVERITY, "double"],
- [SEVERITY, "backtick"],
- [SEVERITY, "single", "avoid-escape"],
- [SEVERITY, "double", "avoid-escape"],
- [SEVERITY, "backtick", "avoid-escape"]]
-};
-
-const errorRulesConfig = {
- "no-unused-vars": [SEVERITY],
- "semi-spacing": [SEVERITY,
- [SEVERITY, { before: true, after: true }],
- [SEVERITY, { before: true, after: false }],
- [SEVERITY, { before: false, after: true }],
- [SEVERITY, { before: false, after: false }]
- ]
-};
-
-describe("autoconfig", () => {
-
- describe("Registry", () => {
-
- it("should set up a registry for rules in a provided rulesConfig", () => {
- const expectedRules = Object.keys(rulesConfig);
- const registry = new autoconfig.Registry(rulesConfig);
-
- assert.strictEqual(Object.keys(registry.rules).length, 3);
- assert.sameMembers(Object.keys(registry.rules), expectedRules);
- assert.isArray(registry.rules.semi);
- assert.isArray(registry.rules["semi-spacing"]);
- assert.isArray(registry.rules.quotes);
- assert.lengthOf(registry.rules.semi, 3);
- assert.lengthOf(registry.rules["semi-spacing"], 5);
- assert.lengthOf(registry.rules.quotes, 7);
- });
-
- it("should not have any rules if constructed without a config argument", () => {
- const registry = new autoconfig.Registry();
-
- assert.isObject(registry.rules);
- assert.lengthOf(Object.keys(registry.rules), 0);
- });
-
- it("should create registryItems for each rule with the proper keys", () => {
- const registry = new autoconfig.Registry(rulesConfig);
-
- assert.isObject(registry.rules.semi[0]);
- assert.isObject(registry.rules["semi-spacing"][0]);
- assert.isObject(registry.rules.quotes[0]);
- assert.property(registry.rules.semi[0], "config");
- assert.property(registry.rules.semi[0], "specificity");
- assert.property(registry.rules.semi[0], "errorCount");
- });
-
- it("should populate the config property correctly", () => {
- const registry = new autoconfig.Registry(rulesConfig);
-
- assert.strictEqual(registry.rules.quotes[0].config, SEVERITY);
- assert.deepStrictEqual(registry.rules.quotes[1].config, [SEVERITY, "single"]);
- assert.deepStrictEqual(registry.rules.quotes[2].config, [SEVERITY, "double"]);
- assert.deepStrictEqual(registry.rules.quotes[3].config, [SEVERITY, "backtick"]);
- assert.deepStrictEqual(registry.rules.quotes[4].config, [SEVERITY, "single", "avoid-escape"]);
- assert.deepStrictEqual(registry.rules.quotes[5].config, [SEVERITY, "double", "avoid-escape"]);
- assert.deepStrictEqual(registry.rules.quotes[6].config, [SEVERITY, "backtick", "avoid-escape"]);
- });
-
- it("should assign the correct specificity", () => {
- const registry = new autoconfig.Registry(rulesConfig);
-
- assert.strictEqual(registry.rules.quotes[0].specificity, 1);
- assert.strictEqual(registry.rules.quotes[1].specificity, 2);
- assert.strictEqual(registry.rules.quotes[6].specificity, 3);
- });
-
- it("should initially leave the errorCount as undefined", () => {
- const registry = new autoconfig.Registry(rulesConfig);
-
- assert.isUndefined(registry.rules.quotes[0].errorCount);
- assert.isUndefined(registry.rules.quotes[1].errorCount);
- assert.isUndefined(registry.rules.quotes[6].errorCount);
- });
-
- describe("populateFromCoreRules()", () => {
-
- it("should add core rules to registry", () => {
- const registry = new autoconfig.Registry();
-
- registry.populateFromCoreRules();
- const finalRuleCount = Object.keys(registry.rules).length;
-
- assert(finalRuleCount > 0);
- assert.include(Object.keys(registry.rules), "eqeqeq");
- });
-
- it("should not add deprecated rules", () => {
- const registry = new autoconfig.Registry();
-
- registry.populateFromCoreRules();
-
- const { rules } = registry;
-
- assert.notProperty(rules, "id-blacklist");
- assert.notProperty(rules, "no-negated-in-lhs");
- assert.notProperty(rules, "no-process-exit");
- assert.notProperty(rules, "no-spaced-func");
- assert.notProperty(rules, "prefer-reflect");
- });
-
- it("should not add duplicate rules", () => {
- const registry = new autoconfig.Registry(rulesConfig);
-
- registry.populateFromCoreRules();
- const semiCount = Object.keys(registry.rules).filter(ruleId => ruleId === "semi").length;
-
- assert.strictEqual(semiCount, 1);
- });
- });
-
- describe("buildRuleSets()", () => {
- let ruleSets;
-
- beforeEach(() => {
- const registry = new autoconfig.Registry(rulesConfig);
-
- ruleSets = registry.buildRuleSets();
- });
-
- it("should create an array of rule configuration sets", () => {
- assert.isArray(ruleSets);
- });
-
- it("should include configs for each rule (at least for the first set)", () => {
- assert.sameMembers(Object.keys(ruleSets[0]), ["semi", "semi-spacing", "quotes"]);
- });
-
- it("should create the first set from default rule configs (severity only)", () => {
- assert.deepStrictEqual(ruleSets[0], { semi: SEVERITY, "semi-spacing": SEVERITY, quotes: SEVERITY });
- });
-
- it("should create as many ruleSets as the highest number of configs in a rule", () => {
-
- // `quotes` has 7 possible configurations
- assert.lengthOf(ruleSets, 7);
- });
- });
-
- describe("lintSourceCode()", () => {
- let registry;
-
- beforeEach(() => {
- const config = { ignore: false };
- const sourceCode = sourceCodeUtils.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME, config);
-
- registry = new autoconfig.Registry(rulesConfig);
- registry = registry.lintSourceCode(sourceCode, defaultOptions);
- });
-
- it("should populate the errorCount of all registryItems", () => {
- const expectedRules = ["semi", "semi-spacing", "quotes"];
-
- assert.sameMembers(Object.keys(registry.rules), expectedRules);
- expectedRules.forEach(ruleId => {
- assert(registry.rules[ruleId].length > 0);
- registry.rules[ruleId].forEach(conf => {
- assert.isNumber(conf.errorCount);
- });
- });
- });
-
- it("should correctly set the error count of configurations", () => {
- assert.strictEqual(registry.rules.semi[0].config, SEVERITY);
- assert.strictEqual(registry.rules.semi[0].errorCount, 0);
- assert.deepStrictEqual(registry.rules.semi[2].config, [SEVERITY, "never"]);
- assert.strictEqual(registry.rules.semi[2].errorCount, 3);
- });
-
- it("should respect inline eslint config comments (and not crash when they make linting errors)", () => {
- const config = { ignore: false };
- const sourceCode = sourceCodeUtils.getSourceCodeOfFiles(CONFIG_COMMENTS_FILENAME, config);
- const expectedRegistry = [
- { config: 2, specificity: 1, errorCount: 3 },
- { config: [2, "always"], specificity: 2, errorCount: 3 },
- { config: [2, "never"], specificity: 2, errorCount: 3 }
- ];
-
- registry = new autoconfig.Registry(rulesConfig);
- registry = registry.lintSourceCode(sourceCode, defaultOptions);
-
- assert.deepStrictEqual(registry.rules.semi, expectedRegistry);
- });
- });
-
- describe("stripFailingConfigs()", () => {
- let registry;
-
- beforeEach(() => {
- const config = { ignore: false };
- const sourceCode = sourceCodeUtils.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME, config);
-
- registry = new autoconfig.Registry(rulesConfig);
- registry = registry.lintSourceCode(sourceCode, defaultOptions);
- registry = registry.stripFailingConfigs();
- });
-
- it("should remove all registryItems with a non-zero errorCount", () => {
- assert.lengthOf(registry.rules.semi, 2);
- assert.lengthOf(registry.rules["semi-spacing"], 3);
- assert.lengthOf(registry.rules.quotes, 1);
- registry.rules.semi.forEach(registryItem => {
- assert.strictEqual(registryItem.errorCount, 0);
- });
- registry.rules["semi-spacing"].forEach(registryItem => {
- assert.strictEqual(registryItem.errorCount, 0);
- });
- registry.rules.quotes.forEach(registryItem => {
- assert.strictEqual(registryItem.errorCount, 0);
- });
- });
- });
-
- describe("getFailingRulesRegistry()", () => {
- let failingRegistry;
-
- beforeEach(() => {
- const config = { ignore: false };
- const sourceCode = sourceCodeUtils.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME, config);
- let registry = new autoconfig.Registry(errorRulesConfig);
-
- registry = registry.lintSourceCode(sourceCode, defaultOptions);
- failingRegistry = registry.getFailingRulesRegistry();
- });
-
- it("should return a registry with no registryItems with an errorCount of zero", () => {
- const failingRules = Object.keys(failingRegistry.rules);
-
- assert.deepStrictEqual(failingRules, ["no-unused-vars"]);
- assert.lengthOf(failingRegistry.rules["no-unused-vars"], 1);
- assert(failingRegistry.rules["no-unused-vars"][0].errorCount > 0);
- });
- });
-
- describe("createConfig()", () => {
- let createdConfig;
-
- beforeEach(() => {
- const config = { ignore: false };
- const sourceCode = sourceCodeUtils.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME, config);
- let registry = new autoconfig.Registry(rulesConfig);
-
- registry = registry.lintSourceCode(sourceCode, defaultOptions);
- registry = registry.stripFailingConfigs();
- createdConfig = registry.createConfig();
- });
-
- it("should create a config with a rules property", () => {
- assert.property(createdConfig, "rules");
- });
-
- it("should add rules which have only one registryItem to the config", () => {
- const configuredRules = Object.keys(createdConfig.rules);
-
- assert.deepStrictEqual(configuredRules, ["quotes"]);
- });
-
- it("should set the configuration of the rule to the registryItem's `config` value", () => {
- assert.deepStrictEqual(createdConfig.rules.quotes, [2, "double", "avoid-escape"]);
- });
-
- it("should not care how many errors the config has", () => {
- const config = { ignore: false };
- const sourceCode = sourceCodeUtils.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME, config);
- let registry = new autoconfig.Registry(errorRulesConfig);
-
- registry = registry.lintSourceCode(sourceCode, defaultOptions);
- const failingRegistry = registry.getFailingRulesRegistry();
-
- createdConfig = failingRegistry.createConfig();
- const configuredRules = Object.keys(createdConfig.rules);
-
- assert.deepStrictEqual(configuredRules, ["no-unused-vars"]);
- });
- });
-
- describe("filterBySpecificity()", () => {
- let registry;
-
- beforeEach(() => {
- registry = new autoconfig.Registry(rulesConfig);
- });
-
- it("should return a registry where all configs have a desired specificity", () => {
- const filteredRegistry1 = registry.filterBySpecificity(1);
- const filteredRegistry2 = registry.filterBySpecificity(2);
- const filteredRegistry3 = registry.filterBySpecificity(3);
-
- assert.lengthOf(filteredRegistry1.rules.semi, 1);
- assert.lengthOf(filteredRegistry1.rules["semi-spacing"], 1);
- assert.lengthOf(filteredRegistry1.rules.quotes, 1);
- assert.lengthOf(filteredRegistry2.rules.semi, 2);
- assert.lengthOf(filteredRegistry2.rules["semi-spacing"], 4);
- assert.lengthOf(filteredRegistry2.rules.quotes, 3);
- assert.lengthOf(filteredRegistry3.rules.quotes, 3);
- });
- });
- });
-
- describe("extendFromRecommended()", () => {
- it("should return a configuration which has `extends` key with Array type value", () => {
- const oldConfig = { extends: [], rules: {} };
- const newConfig = autoconfig.extendFromRecommended(oldConfig);
-
- assert.exists(newConfig.extends);
- assert.isArray(newConfig.extends);
- });
-
- it("should return a configuration which has array property `extends`", () => {
- const oldConfig = { extends: [], rules: {} };
- const newConfig = autoconfig.extendFromRecommended(oldConfig);
-
- assert.include(newConfig.extends, "eslint:recommended");
- });
-
- it("should return a configuration which preserves the previous extending configurations", () => {
- const oldConfig = { extends: ["previous:configuration1", "previous:configuration2"], rules: {} };
- const newConfig = autoconfig.extendFromRecommended(oldConfig);
-
- assert.includeMembers(newConfig.extends, oldConfig.extends);
- });
-
- it("should return a configuration which has `eslint:recommended` at the first of `extends`", () => {
- const oldConfig = { extends: ["previous:configuration1", "previous:configuration2"], rules: {} };
- const newConfig = autoconfig.extendFromRecommended(oldConfig);
- const [firstExtendInNewConfig] = newConfig.extends;
-
- assert.strictEqual(firstExtendInNewConfig, "eslint:recommended");
- });
-
- it("should return a configuration which not includes rules configured in `eslint:recommended`", () => {
- const oldConfig = { extends: [], rules: { ...recommendedConfig.rules } };
- const newConfig = autoconfig.extendFromRecommended(oldConfig);
-
- assert.notInclude(newConfig.rules, oldConfig.rules);
- });
- });
-});
diff --git a/tests/lib/init/config-file.js b/tests/lib/init/config-file.js
deleted file mode 100644
index 50a9af1d5079..000000000000
--- a/tests/lib/init/config-file.js
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * @fileoverview Tests for ConfigFile
- * @author Nicholas C. Zakas
- */
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const assert = require("chai").assert,
- sinon = require("sinon"),
- path = require("path"),
- yaml = require("js-yaml"),
- espree = require("espree"),
- ConfigFile = require("../../../lib/init/config-file"),
- { CLIEngine } = require("../../../lib/cli-engine");
-
-const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Helper function get easily get a path in the fixtures directory.
- * @param {string} filepath The path to find in the fixtures directory.
- * @returns {string} Full path in the fixtures directory.
- * @private
- */
-function getFixturePath(filepath) {
- return path.resolve(__dirname, "../../fixtures/config-file", filepath);
-}
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-describe("ConfigFile", () => {
- describe("write()", () => {
- let config;
-
- beforeEach(() => {
- config = {
- env: {
- browser: true,
- node: true
- },
- rules: {
- quotes: 2,
- semi: 1
- }
- };
- });
-
- afterEach(() => {
- sinon.verifyAndRestore();
- });
-
- [
- ["JavaScript", "foo.js", espree.parse],
- ["JSON", "bar.json", JSON.parse],
- ["YAML", "foo.yaml", yaml.load],
- ["YML", "foo.yml", yaml.load]
- ].forEach(([fileType, filename, validate]) => {
-
- it(`should write a file through fs when a ${fileType} path is passed`, () => {
- const fakeFS = {
- writeFileSync: () => {}
- };
-
- sinon.mock(fakeFS).expects("writeFileSync").withExactArgs(
- filename,
- sinon.match(value => !!validate(value)),
- "utf8"
- );
-
- const StubbedConfigFile = proxyquire("../../../lib/init/config-file", {
- fs: fakeFS
- });
-
- StubbedConfigFile.write(config, filename);
- });
-
- it("should include a newline character at EOF", () => {
- const fakeFS = {
- writeFileSync: () => {}
- };
-
- sinon.mock(fakeFS).expects("writeFileSync").withExactArgs(
- filename,
- sinon.match(value => value.endsWith("\n")),
- "utf8"
- );
-
- const StubbedConfigFile = proxyquire("../../../lib/init/config-file", {
- fs: fakeFS
- });
-
- StubbedConfigFile.write(config, filename);
- });
- });
-
- it("should make sure js config files match linting rules", () => {
- const fakeFS = {
- writeFileSync: () => {}
- };
-
- const singleQuoteConfig = {
- rules: {
- quotes: [2, "single"]
- }
- };
-
- sinon.mock(fakeFS).expects("writeFileSync").withExactArgs(
- "test-config.js",
- sinon.match(value => !value.includes("\"")),
- "utf8"
- );
-
- const StubbedConfigFile = proxyquire("../../../lib/init/config-file", {
- fs: fakeFS
- });
-
- StubbedConfigFile.write(singleQuoteConfig, "test-config.js");
- });
-
- it("should still write a js config file even if linting fails", () => {
- const fakeFS = {
- writeFileSync: () => {}
- };
- const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({
- baseConfig: config,
- fix: true,
- useEslintrc: false
- }));
-
- Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype));
- sinon.stub(fakeCLIEngine.prototype, "executeOnText").throws();
-
- sinon.mock(fakeFS).expects("writeFileSync").once();
-
- const StubbedConfigFile = proxyquire("../../../lib/init/config-file", {
- fs: fakeFS,
- "../cli-engine": { CLIEngine: fakeCLIEngine }
- });
-
- assert.throws(() => {
- StubbedConfigFile.write(config, "test-config.js");
- });
- });
-
- it("should throw error if file extension is not valid", () => {
- assert.throws(() => {
- ConfigFile.write({}, getFixturePath("yaml/.eslintrc.class"));
- }, /write to unknown file type/u);
- });
- });
-});
diff --git a/tests/lib/init/config-initializer.js b/tests/lib/init/config-initializer.js
deleted file mode 100644
index 81e4e52faaa9..000000000000
--- a/tests/lib/init/config-initializer.js
+++ /dev/null
@@ -1,577 +0,0 @@
-/**
- * @fileoverview Tests for configInitializer.
- * @author Ilya Volodin
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const assert = require("chai").assert,
- fs = require("fs"),
- path = require("path"),
- os = require("os"),
- sinon = require("sinon"),
- sh = require("shelljs"),
- espree = require("espree"),
- autoconfig = require("../../../lib/init/autoconfig"),
- npmUtils = require("../../../lib/init/npm-utils");
-
-const originalDir = process.cwd();
-const proxyquire = require("proxyquire").noPreserveCache();
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-let answers = {};
-let pkgJSONContents = {};
-let pkgJSONPath = "";
-
-describe("configInitializer", () => {
-
- let fixtureDir,
- npmCheckStub,
- npmInstallStub,
- npmFetchPeerDependenciesStub,
- init,
- localESLintVersion = null;
-
- const log = {
- info: sinon.spy(),
- error: sinon.spy()
- };
- const requireStubs = {
- "../shared/logging": log,
- "../shared/relative-module-resolver": {
- resolve() {
- if (localESLintVersion) {
- return `local-eslint-${localESLintVersion}`;
- }
- throw new Error("Cannot find module");
- }
- },
- "local-eslint-3.18.0": { linter: { version: "3.18.0" }, "@noCallThru": true },
- "local-eslint-3.19.0": { linter: { version: "3.19.0" }, "@noCallThru": true },
- "local-eslint-4.0.0": { linter: { version: "4.0.0" }, "@noCallThru": true }
- };
-
- /**
- * Returns the path inside of the fixture directory.
- * @param {...string} args file path segments.
- * @returns {string} The path inside the fixture directory.
- * @private
- */
- function getFixturePath(...args) {
- const filepath = path.join(fixtureDir, ...args);
-
- try {
- return fs.realpathSync(filepath);
- } catch {
- return filepath;
- }
- }
-
- // copy into clean area so as not to get "infected" by this project's .eslintrc files
- before(() => {
- fixtureDir = path.join(os.tmpdir(), "eslint/fixtures/config-initializer");
- sh.mkdir("-p", fixtureDir);
- sh.cp("-r", "./tests/fixtures/config-initializer/.", fixtureDir);
- fixtureDir = fs.realpathSync(fixtureDir);
- });
-
- beforeEach(() => {
- npmInstallStub = sinon.stub(npmUtils, "installSyncSaveDev");
- npmCheckStub = sinon.stub(npmUtils, "checkDevDeps").callsFake(packages => packages.reduce((status, pkg) => {
- status[pkg] = false;
- return status;
- }, {}));
- npmFetchPeerDependenciesStub = sinon
- .stub(npmUtils, "fetchPeerDependencies")
- .returns({
- eslint: "^3.19.0",
- "eslint-plugin-jsx-a11y": "^5.0.1",
- "eslint-plugin-import": "^2.2.0",
- "eslint-plugin-react": "^7.0.1"
- });
- init = proxyquire("../../../lib/init/config-initializer", requireStubs);
- });
-
- afterEach(() => {
- log.info.resetHistory();
- log.error.resetHistory();
- npmInstallStub.restore();
- npmCheckStub.restore();
- npmFetchPeerDependenciesStub.restore();
- });
-
- after(() => {
- sh.rm("-r", fixtureDir);
- });
-
- describe("processAnswers()", () => {
-
- describe("prompt", () => {
-
- beforeEach(() => {
- answers = {
- purpose: "style",
- source: "prompt",
- extendDefault: true,
- indent: 2,
- quotes: "single",
- linebreak: "unix",
- semi: true,
- moduleType: "esm",
- es6Globals: true,
- env: ["browser"],
- format: "JSON"
- };
- });
-
- it("should create default config", () => {
- const config = init.processAnswers(answers);
-
- assert.deepStrictEqual(config.rules.indent, ["error", 2]);
- assert.deepStrictEqual(config.rules.quotes, ["error", "single"]);
- assert.deepStrictEqual(config.rules["linebreak-style"], ["error", "unix"]);
- assert.deepStrictEqual(config.rules.semi, ["error", "always"]);
- assert.strictEqual(config.env.es2021, true);
- assert.strictEqual(config.parserOptions.ecmaVersion, espree.latestEcmaVersion);
- assert.strictEqual(config.parserOptions.sourceType, "module");
- assert.strictEqual(config.env.browser, true);
- assert.strictEqual(config.extends, "eslint:recommended");
- });
-
- it("should disable semi", () => {
- answers.semi = false;
- const config = init.processAnswers(answers);
-
- assert.deepStrictEqual(config.rules.semi, ["error", "never"]);
- });
-
- it("should enable react plugin", () => {
- answers.framework = "react";
- const config = init.processAnswers(answers);
-
- assert.strictEqual(config.parserOptions.ecmaFeatures.jsx, true);
- assert.strictEqual(config.parserOptions.ecmaVersion, espree.latestEcmaVersion);
- assert.deepStrictEqual(config.plugins, ["react"]);
- });
-
- it("should enable vue plugin", () => {
- answers.framework = "vue";
- const config = init.processAnswers(answers);
-
- assert.strictEqual(config.parserOptions.ecmaVersion, espree.latestEcmaVersion);
- assert.deepStrictEqual(config.plugins, ["vue"]);
- assert.deepStrictEqual(config.extends, ["eslint:recommended", "plugin:vue/essential"]);
- });
-
- it("should enable typescript parser and plugin", () => {
- answers.typescript = true;
- const config = init.processAnswers(answers);
-
- assert.strictEqual(config.parser, "@typescript-eslint/parser");
- assert.deepStrictEqual(config.plugins, ["@typescript-eslint"]);
- assert.deepStrictEqual(config.extends, ["eslint:recommended", "plugin:@typescript-eslint/recommended"]);
- });
-
- it("should enable typescript parser and plugin with vue", () => {
- answers.framework = "vue";
- answers.typescript = true;
- const config = init.processAnswers(answers);
-
- assert.deepStrictEqual(config.extends, ["eslint:recommended", "plugin:vue/essential", "plugin:@typescript-eslint/recommended"]);
- assert.strictEqual(config.parserOptions.parser, "@typescript-eslint/parser");
- assert.deepStrictEqual(config.plugins, ["vue", "@typescript-eslint"]);
- });
-
- it("should extend eslint:recommended", () => {
- const config = init.processAnswers(answers);
-
- assert.strictEqual(config.extends, "eslint:recommended");
- });
-
- it("should not use commonjs by default", () => {
- const config = init.processAnswers(answers);
-
- assert.isUndefined(config.env.commonjs);
- });
-
- it("should use commonjs when set", () => {
- answers.moduleType = "commonjs";
- const config = init.processAnswers(answers);
-
- assert.isTrue(config.env.commonjs);
- });
- });
-
- describe("guide", () => {
- it("should support the google style guide", () => {
- const config = { extends: "google" };
- const modules = init.getModulesList(config);
-
- assert.deepStrictEqual(config, { extends: "google", installedESLint: true });
- assert.include(modules, "eslint-config-google@latest");
- });
-
- it("should support the airbnb style guide", () => {
- const config = { extends: "airbnb" };
- const modules = init.getModulesList(config);
-
- assert.deepStrictEqual(config, { extends: "airbnb", installedESLint: true });
- assert.include(modules, "eslint-config-airbnb@latest");
- });
-
- it("should support the airbnb base style guide", () => {
- const config = { extends: "airbnb-base" };
- const modules = init.getModulesList(config);
-
- assert.deepStrictEqual(config, { extends: "airbnb-base", installedESLint: true });
- assert.include(modules, "eslint-config-airbnb-base@latest");
- });
-
- it("should support the standard style guide", () => {
- const config = { extends: "standard" };
- const modules = init.getModulesList(config);
-
- assert.deepStrictEqual(config, { extends: "standard", installedESLint: true });
- assert.include(modules, "eslint-config-standard@latest");
- });
-
- it("should support the xo style guide", () => {
- const config = { extends: "xo" };
- const modules = init.getModulesList(config);
-
- assert.deepStrictEqual(config, { extends: "xo", installedESLint: true });
- assert.include(modules, "eslint-config-xo@latest");
- });
-
- it("should install required sharable config", () => {
- const config = { extends: "google" };
-
- init.installModules(init.getModulesList(config));
- assert(npmInstallStub.calledOnce);
- assert(npmInstallStub.firstCall.args[0].some(name => name.startsWith("eslint-config-google@")));
- });
-
- it("should install ESLint if not installed locally", () => {
- const config = { extends: "google" };
-
- init.installModules(init.getModulesList(config));
- assert(npmInstallStub.calledOnce);
- assert(npmInstallStub.firstCall.args[0].some(name => name.startsWith("eslint@")));
- });
-
- it("should install peerDependencies of the sharable config", () => {
- const config = { extends: "airbnb" };
-
- init.installModules(init.getModulesList(config));
-
- assert(npmFetchPeerDependenciesStub.calledOnce);
- assert(npmFetchPeerDependenciesStub.firstCall.args[0] === "eslint-config-airbnb@latest");
- assert(npmInstallStub.calledOnce);
- assert.deepStrictEqual(
- npmInstallStub.firstCall.args[0],
- [
- "eslint-config-airbnb@latest",
- "eslint@^3.19.0",
- "eslint-plugin-jsx-a11y@^5.0.1",
- "eslint-plugin-import@^2.2.0",
- "eslint-plugin-react@^7.0.1"
- ]
- );
- });
-
- describe("hasESLintVersionConflict (Note: peerDependencies always `eslint: \"^3.19.0\"` by stubs)", () => {
- describe("if local ESLint is not found,", () => {
- before(() => {
- localESLintVersion = null;
- });
-
- it("should return false.", () => {
- const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
-
- assert.strictEqual(result, false);
- });
- });
-
- describe("if local ESLint is 3.19.0,", () => {
- before(() => {
- localESLintVersion = "3.19.0";
- });
-
- it("should return false.", () => {
- const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
-
- assert.strictEqual(result, false);
- });
- });
-
- describe("if local ESLint is 4.0.0,", () => {
- before(() => {
- localESLintVersion = "4.0.0";
- });
-
- it("should return true.", () => {
- const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
-
- assert.strictEqual(result, true);
- });
- });
-
- describe("if local ESLint is 3.18.0,", () => {
- before(() => {
- localESLintVersion = "3.18.0";
- });
-
- it("should return true.", () => {
- const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
-
- assert.strictEqual(result, true);
- });
- });
- });
-
- it("should support the standard style guide with Vue.js", () => {
- const config = {
- plugins: ["vue"],
- extends: ["plugin:vue/essential", "standard"]
- };
- const modules = init.getModulesList(config);
-
- assert.include(modules, "eslint-plugin-vue@latest");
- assert.include(modules, "eslint-config-standard@latest");
- });
-
- it("should support custom parser", () => {
- const config = {
- parser: "@typescript-eslint/parser"
- };
- const modules = init.getModulesList(config);
-
- assert.include(modules, "@typescript-eslint/parser@latest");
- });
-
- it("should support custom parser with Vue.js", () => {
- const config = {
-
- // We should declare the parser at `parserOptions` when using with `eslint-plugin-vue`.
- parserOptions: {
- parser: "@typescript-eslint/parser"
- }
- };
- const modules = init.getModulesList(config);
-
- assert.include(modules, "@typescript-eslint/parser@latest");
- });
- });
-
- describe("auto", () => {
- const completeSpy = sinon.spy();
- let config;
-
- before(() => {
- const patterns = [
- getFixturePath("lib"),
- getFixturePath("tests")
- ].join(" ");
-
- answers = {
- purpose: "style",
- source: "auto",
- patterns,
- env: ["browser"],
- format: "JSON"
- };
-
- sinon.stub(console, "log"); // necessary to replace, because of progress bar
-
- process.chdir(fixtureDir);
- config = init.processAnswers(answers);
- sinon.restore();
- });
-
- after(() => {
- sinon.restore();
- });
-
- afterEach(() => {
- process.chdir(originalDir);
- sinon.restore();
- });
-
- it("should create a config", () => {
- assert.isTrue(completeSpy.notCalled);
- assert.ok(config);
- });
-
- it("should create the config based on examined files", () => {
- assert.deepStrictEqual(config.rules.quotes, ["error", "double"]);
- assert.strictEqual(config.rules.semi, "off");
- });
-
- it("should extend and not disable recommended rules", () => {
- assert.strictEqual(config.extends, "eslint:recommended");
- assert.notProperty(config.rules, "no-debugger");
- });
-
- it("should not include deprecated rules", () => {
- assert.notProperty(config.rules, "id-blacklist");
- assert.notProperty(config.rules, "no-negated-in-lhs");
- assert.notProperty(config.rules, "no-process-exit");
- assert.notProperty(config.rules, "no-spaced-func");
- assert.notProperty(config.rules, "prefer-reflect");
- });
-
- it("should support new ES features if using later ES version", () => {
- const filename = getFixturePath("new-es-features");
-
- answers.patterns = filename;
- answers.ecmaVersion = 2017;
- process.chdir(fixtureDir);
- config = init.processAnswers(answers);
- });
-
- it("should throw on fatal parsing error", () => {
- const filename = getFixturePath("parse-error");
-
- sinon.stub(autoconfig, "extendFromRecommended");
- answers.patterns = filename;
- process.chdir(fixtureDir);
- assert.throws(() => {
- config = init.processAnswers(answers);
- }, "Parsing error: Unexpected token ;");
- });
-
- it("should throw if no files are matched from patterns", () => {
- sinon.stub(autoconfig, "extendFromRecommended");
- answers.patterns = "not-a-real-filename";
- process.chdir(fixtureDir);
- assert.throws(() => {
- config = init.processAnswers(answers);
- }, "No files matching 'not-a-real-filename' were found.");
- });
- });
- });
-
- describe("writeFile()", () => {
-
- beforeEach(() => {
- answers = {
- purpose: "style",
- source: "prompt",
- extendDefault: true,
- indent: 2,
- quotes: "single",
- linebreak: "unix",
- semi: true,
- moduleType: "esm",
- es6Globals: true,
- env: ["browser"],
- format: "JSON"
- };
-
- pkgJSONContents = {
- name: "config-initializer",
- version: "1.0.0"
- };
-
- process.chdir(fixtureDir);
-
- pkgJSONPath = path.resolve(fixtureDir, "package.json");
- });
-
- afterEach(() => {
- process.chdir(originalDir);
- });
-
- it("should create .eslintrc.json", () => {
- const config = init.processAnswers(answers);
- const filePath = path.resolve(fixtureDir, ".eslintrc.json");
-
- fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
-
- init.writeFile(config, answers.format);
-
- assert.isTrue(fs.existsSync(filePath));
-
- fs.unlinkSync(filePath);
- fs.unlinkSync(pkgJSONPath);
- });
-
- it("should create .eslintrc.js", () => {
- answers.format = "JavaScript";
-
- const config = init.processAnswers(answers);
- const filePath = path.resolve(fixtureDir, ".eslintrc.js");
-
- fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
-
- init.writeFile(config, answers.format);
-
- assert.isTrue(fs.existsSync(filePath));
-
- fs.unlinkSync(filePath);
- fs.unlinkSync(pkgJSONPath);
- });
-
- it("should create .eslintrc.yml", () => {
- answers.format = "YAML";
-
- const config = init.processAnswers(answers);
- const filePath = path.resolve(fixtureDir, ".eslintrc.yml");
-
- fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
-
- init.writeFile(config, answers.format);
-
- assert.isTrue(fs.existsSync(filePath));
-
- fs.unlinkSync(filePath);
- fs.unlinkSync(pkgJSONPath);
- });
-
- // For https://github.com/eslint/eslint/issues/14137
- it("should create .eslintrc.cjs", () => {
- answers.format = "JavaScript";
-
- // create package.json with "type": "module"
- pkgJSONContents.type = "module";
-
- fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
-
- const config = init.processAnswers(answers);
- const filePath = path.resolve(fixtureDir, ".eslintrc.cjs");
-
- init.writeFile(config, answers.format);
-
- assert.isTrue(fs.existsSync(filePath));
-
- fs.unlinkSync(filePath);
- fs.unlinkSync(pkgJSONPath);
- });
-
- it("should create .eslintrc.json even with type: 'module'", () => {
- answers.format = "JSON";
-
- // create package.json with "type": "module"
- pkgJSONContents.type = "module";
-
- fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
-
- const config = init.processAnswers(answers);
- const filePath = path.resolve(fixtureDir, ".eslintrc.json");
-
- init.writeFile(config, answers.format);
-
- assert.isTrue(fs.existsSync(filePath));
-
- fs.unlinkSync(filePath);
- fs.unlinkSync(pkgJSONPath);
- });
- });
-});
diff --git a/tests/lib/init/config-rule.js b/tests/lib/init/config-rule.js
deleted file mode 100644
index 425317dd2c8a..000000000000
--- a/tests/lib/init/config-rule.js
+++ /dev/null
@@ -1,329 +0,0 @@
-/**
- * @fileoverview Tests for ConfigOps
- * @author Ian VanSchooten
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const assert = require("chai").assert,
- ConfigRule = require("../../../lib/init/config-rule"),
- builtInRules = require("../../../lib/rules"),
- schema = require("../../fixtures/config-rule/schemas");
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-const SEVERITY = 2;
-
-describe("ConfigRule", () => {
-
- describe("generateConfigsFromSchema()", () => {
- let actualConfigs;
-
- it("should create a config with only severity for an empty schema", () => {
- actualConfigs = ConfigRule.generateConfigsFromSchema([]);
- assert.deepStrictEqual(actualConfigs, [SEVERITY]);
- });
-
- it("should create a config with only severity with no arguments", () => {
- actualConfigs = ConfigRule.generateConfigsFromSchema();
- assert.deepStrictEqual(actualConfigs, [SEVERITY]);
- });
-
- describe("for a single enum schema", () => {
-
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.enum);
- });
-
- it("should create an array of configs", () => {
- assert.isArray(actualConfigs);
- assert.strictEqual(actualConfigs.length, 3);
- });
-
- it("should include the error severity (2) without options as the first config", () => {
- assert.strictEqual(actualConfigs[0], SEVERITY);
- });
-
- it("should set all configs to error severity (2)", () => {
- actualConfigs.forEach(actualConfig => {
- if (Array.isArray(actualConfig)) {
- assert.strictEqual(actualConfig[0], SEVERITY);
- }
- });
- });
-
- it("should return configs with each enumerated value in the schema", () => {
- assert.sameDeepMembers(actualConfigs, [SEVERITY, [SEVERITY, "always"], [SEVERITY, "never"]]);
- });
- });
-
- describe("for a object schema with a single enum property", () => {
-
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.objectWithEnum);
- });
-
- it("should return configs with option objects", () => {
-
- // Skip first config (severity only)
- actualConfigs.slice(1).forEach(actualConfig => {
- const actualConfigOption = actualConfig[1]; // severity is first element, option is second
-
- assert.isObject(actualConfigOption);
- });
- });
-
- it("should use the object property name from the schema", () => {
- const propName = "enumProperty";
-
- assert.strictEqual(actualConfigs.length, 3);
- actualConfigs.slice(1).forEach(actualConfig => {
- const actualConfigOption = actualConfig[1];
-
- assert.property(actualConfigOption, propName);
- });
- });
-
- it("should have each enum as option object values", () => {
- const propName = "enumProperty",
- actualValues = [];
-
- actualConfigs.slice(1).forEach(actualConfig => {
- const configOption = actualConfig[1];
-
- actualValues.push(configOption[propName]);
- });
- assert.sameMembers(actualValues, ["always", "never"]);
- });
- });
-
- describe("for a object schema with a multiple enum properties", () => {
-
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.objectWithMultipleEnums);
- });
-
- it("should create configs for all properties in each config", () => {
- const expectedProperties = ["firstEnum", "anotherEnum"];
-
- assert.strictEqual(actualConfigs.length, 7);
- actualConfigs.slice(1).forEach(actualConfig => {
- const configOption = actualConfig[1];
- const actualProperties = Object.keys(configOption);
-
- assert.sameMembers(actualProperties, expectedProperties);
- });
- });
-
- it("should create configs for every possible combination", () => {
- const expectedConfigs = [
- { firstEnum: "always", anotherEnum: "var" },
- { firstEnum: "always", anotherEnum: "let" },
- { firstEnum: "always", anotherEnum: "const" },
- { firstEnum: "never", anotherEnum: "var" },
- { firstEnum: "never", anotherEnum: "let" },
- { firstEnum: "never", anotherEnum: "const" }
- ];
- const actualConfigOptions = actualConfigs.slice(1).map(actualConfig => actualConfig[1]);
-
- assert.sameDeepMembers(actualConfigOptions, expectedConfigs);
- });
-
- });
-
- describe("for a object schema with a single boolean property", () => {
-
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.objectWithBool);
- });
-
- it("should return configs with option objects", () => {
- assert.strictEqual(actualConfigs.length, 3);
- actualConfigs.slice(1).forEach(actualConfig => {
- const actualConfigOption = actualConfig[1];
-
- assert.isObject(actualConfigOption);
- });
- });
-
- it("should use the object property name from the schema", () => {
- const propName = "boolProperty";
-
- assert.strictEqual(actualConfigs.length, 3);
- actualConfigs.slice(1).forEach(actualConfig => {
- const actualConfigOption = actualConfig[1];
-
- assert.property(actualConfigOption, propName);
- });
- });
-
- it("should include both true and false configs", () => {
- const propName = "boolProperty",
- actualValues = [];
-
- actualConfigs.slice(1).forEach(actualConfig => {
- const configOption = actualConfig[1];
-
- actualValues.push(configOption[propName]);
- });
- assert.sameMembers(actualValues, [true, false]);
- });
- });
-
- describe("for a object schema with a multiple bool properties", () => {
-
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.objectWithMultipleBools);
- });
-
- it("should create configs for all properties in each config", () => {
- const expectedProperties = ["firstBool", "anotherBool"];
-
- assert.strictEqual(actualConfigs.length, 5);
- actualConfigs.slice(1).forEach(config => {
- const configOption = config[1];
- const actualProperties = Object.keys(configOption);
-
- assert.sameMembers(actualProperties, expectedProperties);
- });
- });
-
- it("should create configs for every possible combination", () => {
- const expectedConfigOptions = [
- { firstBool: true, anotherBool: true },
- { firstBool: true, anotherBool: false },
- { firstBool: false, anotherBool: true },
- { firstBool: false, anotherBool: false }
- ];
- const actualConfigOptions = actualConfigs.slice(1).map(config => config[1]);
-
- assert.sameDeepMembers(actualConfigOptions, expectedConfigOptions);
- });
- });
-
- describe("for a schema with an enum and an object", () => {
-
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.mixedEnumObject);
- });
-
- it("should create configs with only the enum values", () => {
- assert.strictEqual(actualConfigs[1].length, 2);
- assert.strictEqual(actualConfigs[2].length, 2);
- const actualOptions = [actualConfigs[1][1], actualConfigs[2][1]];
-
- assert.sameMembers(actualOptions, ["always", "never"]);
- });
-
- it("should create configs with a string and an object", () => {
- assert.strictEqual(actualConfigs.length, 7);
- actualConfigs.slice(3).forEach(config => {
- assert.isString(config[1]);
- assert.isObject(config[2]);
- });
- });
- });
-
- describe("for a schema with an enum followed by an object with no usable properties", () => {
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.mixedEnumObjectWithNothing);
- });
-
- it("should create config only for the enum", () => {
- const expectedConfigs = [2, [2, "always"], [2, "never"]];
-
- assert.sameDeepMembers(actualConfigs, expectedConfigs);
- });
- });
-
- describe("for a schema with an enum preceded by an object with no usable properties", () => {
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.mixedObjectWithNothingEnum);
- });
-
- it("should not create a config for the enum", () => {
- const expectedConfigs = [2];
-
- assert.sameDeepMembers(actualConfigs, expectedConfigs);
- });
- });
-
- describe("for a schema with an enum preceded by a string", () => {
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.mixedStringEnum);
- });
-
- it("should not create a config for the enum", () => {
- const expectedConfigs = [2];
-
- assert.sameDeepMembers(actualConfigs, expectedConfigs);
- });
- });
-
- describe("for a schema with oneOf", () => {
-
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.oneOf);
- });
-
- it("should create a set of configs", () => {
- assert.isArray(actualConfigs);
- });
- });
-
- describe("for a schema with nested objects", () => {
-
- before(() => {
- actualConfigs = ConfigRule.generateConfigsFromSchema(schema.nestedObjects);
- });
-
- it("should create a set of configs", () => {
- assert.isArray(actualConfigs);
- });
- });
- });
-
- describe("createCoreRuleConfigs()", () => {
-
- const rulesConfig = ConfigRule.createCoreRuleConfigs();
-
- it("should create a rulesConfig containing all core rules", () => {
- const
- expectedRules = Array.from(builtInRules.keys()),
- actualRules = Object.keys(rulesConfig);
-
- assert.sameMembers(actualRules, expectedRules);
- });
-
- it("should allow to ignore deprecated rules", () => {
- const expectedRules = Array.from(builtInRules.entries())
- .filter(([, rule]) => {
- const isDeprecated = (typeof rule === "function") ? rule.deprecated : rule.meta.deprecated;
-
- return !isDeprecated;
- })
- .map(([id]) => id),
- actualRules = Object.keys(ConfigRule.createCoreRuleConfigs(true));
-
- assert.sameMembers(actualRules, expectedRules);
-
- // Make sure it doesn't contain deprecated rules.
- assert.notInclude(actualRules, "newline-after-var");
- });
-
- it("should create arrays of configs for rules", () => {
- assert.isArray(rulesConfig.quotes);
- assert.include(rulesConfig.quotes, 2);
- });
-
- it("should create configs for rules with meta", () => {
- assert(rulesConfig["accessor-pairs"].length > 1);
- });
- });
-});
diff --git a/tests/lib/init/npm-utils.js b/tests/lib/init/npm-utils.js
deleted file mode 100644
index 8465796a367f..000000000000
--- a/tests/lib/init/npm-utils.js
+++ /dev/null
@@ -1,228 +0,0 @@
-/**
- * @fileoverview Tests for rule fixer.
- * @author Ian VanSchooten
- */
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const
- assert = require("chai").assert,
- spawn = require("cross-spawn"),
- sinon = require("sinon"),
- npmUtils = require("../../../lib/init/npm-utils"),
- log = require("../../../lib/shared/logging"),
- { defineInMemoryFs } = require("../../_utils");
-
-const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
-
-//------------------------------------------------------------------------------
-// Helpers
-//------------------------------------------------------------------------------
-
-/**
- * Import `npm-utils` with the in-memory file system.
- * @param {Object} files The file definitions.
- * @returns {Object} `npm-utils`.
- */
-function requireNpmUtilsWithInMemoryFileSystem(files) {
- const fs = defineInMemoryFs({ files });
-
- return proxyquire("../../../lib/init/npm-utils", { fs });
-}
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-describe("npmUtils", () => {
- afterEach(() => {
- sinon.verifyAndRestore();
- });
-
- describe("checkDevDeps()", () => {
- let installStatus;
-
- before(() => {
- installStatus = npmUtils.checkDevDeps(["debug", "mocha", "notarealpackage", "jshint"]);
- });
-
- it("should not find a direct dependency of the project", () => {
- assert.isFalse(installStatus.debug);
- });
-
- it("should find a dev dependency of the project", () => {
- assert.isTrue(installStatus.mocha);
- });
-
- it("should not find non-dependencies", () => {
- assert.isFalse(installStatus.notarealpackage);
- });
-
- it("should not find nested dependencies", () => {
- assert.isFalse(installStatus.jshint);
- });
-
- it("should return false for a single, non-existent package", () => {
- installStatus = npmUtils.checkDevDeps(["notarealpackage"]);
- assert.isFalse(installStatus.notarealpackage);
- });
-
- it("should handle missing devDependencies key", () => {
- const stubbedNpmUtils = requireNpmUtilsWithInMemoryFileSystem({
- "package.json": JSON.stringify({ private: true, dependencies: {} })
- });
-
- // Should not throw.
- stubbedNpmUtils.checkDevDeps(["some-package"]);
- });
-
- it("should throw with message when parsing invalid package.json", () => {
- const stubbedNpmUtils = requireNpmUtilsWithInMemoryFileSystem({
- "package.json": "{ \"not: \"valid json\" }"
- });
-
- assert.throws(() => {
- try {
- stubbedNpmUtils.checkDevDeps(["some-package"]);
- } catch (error) {
- assert.strictEqual(error.messageTemplate, "failed-to-read-json");
- throw error;
- }
- }, "SyntaxError: Unexpected token v");
- });
- });
-
- describe("checkDeps()", () => {
- let installStatus;
-
- before(() => {
- installStatus = npmUtils.checkDeps(["debug", "mocha", "notarealpackage", "jshint"]);
- });
-
- it("should find a direct dependency of the project", () => {
- assert.isTrue(installStatus.debug);
- });
-
- it("should not find a dev dependency of the project", () => {
- assert.isFalse(installStatus.mocha);
- });
-
- it("should not find non-dependencies", () => {
- assert.isFalse(installStatus.notarealpackage);
- });
-
- it("should not find nested dependencies", () => {
- assert.isFalse(installStatus.jshint);
- });
-
- it("should return false for a single, non-existent package", () => {
- installStatus = npmUtils.checkDeps(["notarealpackage"]);
- assert.isFalse(installStatus.notarealpackage);
- });
-
- it("should throw if no package.json can be found", () => {
- assert.throws(() => {
- installStatus = npmUtils.checkDeps(["notarealpackage"], "/fakepath");
- }, "Could not find a package.json file");
- });
-
- it("should handle missing dependencies key", () => {
- const stubbedNpmUtils = requireNpmUtilsWithInMemoryFileSystem({
- "package.json": JSON.stringify({ private: true, devDependencies: {} })
- });
-
- // Should not throw.
- stubbedNpmUtils.checkDeps(["some-package"]);
- });
-
- it("should throw with message when parsing invalid package.json", () => {
- const stubbedNpmUtils = requireNpmUtilsWithInMemoryFileSystem({
- "package.json": "{ \"not: \"valid json\" }"
- });
-
- assert.throws(() => {
- try {
- stubbedNpmUtils.checkDeps(["some-package"]);
- } catch (error) {
- assert.strictEqual(error.messageTemplate, "failed-to-read-json");
- throw error;
- }
- }, "SyntaxError: Unexpected token v");
- });
- });
-
- describe("checkPackageJson()", () => {
- it("should return true if package.json exists", () => {
- const stubbedNpmUtils = requireNpmUtilsWithInMemoryFileSystem({
- "package.json": "{ \"file\": \"contents\" }"
- });
-
- assert.strictEqual(stubbedNpmUtils.checkPackageJson(), true);
- });
-
- it("should return false if package.json does not exist", () => {
- const stubbedNpmUtils = requireNpmUtilsWithInMemoryFileSystem({});
-
- assert.strictEqual(stubbedNpmUtils.checkPackageJson(), false);
- });
- });
-
- describe("installSyncSaveDev()", () => {
- it("should invoke npm to install a single desired package", () => {
- const stub = sinon.stub(spawn, "sync").returns({ stdout: "" });
-
- npmUtils.installSyncSaveDev("desired-package");
- assert(stub.calledOnce);
- assert.strictEqual(stub.firstCall.args[0], "npm");
- assert.deepStrictEqual(stub.firstCall.args[1], ["i", "--save-dev", "desired-package"]);
- stub.restore();
- });
-
- it("should accept an array of packages to install", () => {
- const stub = sinon.stub(spawn, "sync").returns({ stdout: "" });
-
- npmUtils.installSyncSaveDev(["first-package", "second-package"]);
- assert(stub.calledOnce);
- assert.strictEqual(stub.firstCall.args[0], "npm");
- assert.deepStrictEqual(stub.firstCall.args[1], ["i", "--save-dev", "first-package", "second-package"]);
- stub.restore();
- });
-
- it("should log an error message if npm throws ENOENT error", () => {
- const logErrorStub = sinon.stub(log, "error");
- const npmUtilsStub = sinon.stub(spawn, "sync").returns({ error: { code: "ENOENT" } });
-
- npmUtils.installSyncSaveDev("some-package");
-
- assert(logErrorStub.calledOnce);
-
- logErrorStub.restore();
- npmUtilsStub.restore();
- });
- });
-
- describe("fetchPeerDependencies()", () => {
- it("should execute 'npm show --json peerDependencies' command", () => {
- const stub = sinon.stub(spawn, "sync").returns({ stdout: "" });
-
- npmUtils.fetchPeerDependencies("desired-package");
- assert(stub.calledOnce);
- assert.strictEqual(stub.firstCall.args[0], "npm");
- assert.deepStrictEqual(stub.firstCall.args[1], ["show", "--json", "desired-package", "peerDependencies"]);
- stub.restore();
- });
-
- it("should return null if npm throws ENOENT error", () => {
- const stub = sinon.stub(spawn, "sync").returns({ error: { code: "ENOENT" } });
-
- const peerDependencies = npmUtils.fetchPeerDependencies("desired-package");
-
- assert.isNull(peerDependencies);
-
- stub.restore();
- });
- });
-});
diff --git a/tests/lib/init/source-code-utils.js b/tests/lib/init/source-code-utils.js
deleted file mode 100644
index 994d23d31688..000000000000
--- a/tests/lib/init/source-code-utils.js
+++ /dev/null
@@ -1,250 +0,0 @@
-/**
- * @fileoverview Tests for source-code-util.
- * @author Ian VanSchooten
- */
-
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const path = require("path"),
- fs = require("fs"),
- os = require("os"),
- assert = require("chai").assert,
- sinon = require("sinon"),
- sh = require("shelljs"),
- { SourceCode } = require("../../../lib/source-code");
-
-const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
-const originalDir = process.cwd();
-
-//------------------------------------------------------------------------------
-// Tests
-//------------------------------------------------------------------------------
-
-describe("SourceCodeUtil", () => {
-
- let fixtureDir,
- getSourceCodeOfFiles;
-
- /**
- * Returns the path inside of the fixture directory.
- * @param {...string} args file path segments.
- * @returns {string} The path inside the fixture directory.
- * @private
- */
- function getFixturePath(...args) {
- let filepath = path.join(fixtureDir, ...args);
-
- try {
- filepath = fs.realpathSync(filepath);
- return filepath;
- } catch {
- return filepath;
- }
- }
-
- const log = {
- info: sinon.spy(),
- error: sinon.spy()
- };
- const requireStubs = {
- "../logging": log
- };
-
- // copy into clean area so as not to get "infected" by this project's .eslintrc files
- before(() => {
- fixtureDir = `${os.tmpdir()}/eslint/fixtures/source-code-util`;
- sh.mkdir("-p", fixtureDir);
- sh.cp("-r", "./tests/fixtures/source-code-util/.", fixtureDir);
- fixtureDir = fs.realpathSync(fixtureDir);
- });
-
- beforeEach(() => {
- getSourceCodeOfFiles = proxyquire("../../../lib/init/source-code-utils", requireStubs).getSourceCodeOfFiles;
- });
-
- afterEach(() => {
- log.info.resetHistory();
- log.error.resetHistory();
- });
-
- after(() => {
- sh.rm("-r", fixtureDir);
- });
-
- describe("getSourceCodeOfFiles()", () => {
-
- it("should handle single string filename arguments", () => {
- const filename = getFixturePath("foo.js");
- const sourceCode = getSourceCodeOfFiles(filename, { cwd: fixtureDir });
-
- assert.isObject(sourceCode);
- });
-
- it("should accept an array of string filenames", () => {
- const fooFilename = getFixturePath("foo.js");
- const barFilename = getFixturePath("bar.js");
- const sourceCode = getSourceCodeOfFiles([fooFilename, barFilename], { cwd: fixtureDir });
-
- assert.isObject(sourceCode);
- });
-
- it("should accept a glob argument", () => {
- const glob = getFixturePath("*.js");
- const filename = getFixturePath("foo.js");
- const sourceCode = getSourceCodeOfFiles(glob, { cwd: fixtureDir });
-
- assert.isObject(sourceCode);
- assert.property(sourceCode, filename);
- });
-
- it("should accept a relative filename", () => {
- const filename = "foo.js";
- const sourceCode = getSourceCodeOfFiles(filename, { cwd: fixtureDir });
-
- assert.isObject(sourceCode);
- assert.property(sourceCode, getFixturePath(filename));
- });
-
- it("should accept a relative path to a file in a parent directory", () => {
- const filename = "../foo.js";
- const sourceCode = getSourceCodeOfFiles(filename, { cwd: getFixturePath("nested") });
-
- assert.isObject(sourceCode);
- assert.property(sourceCode, getFixturePath("foo.js"));
- });
-
- it("should accept a callback", () => {
- const filename = getFixturePath("foo.js");
- const spy = sinon.spy();
-
- process.chdir(fixtureDir);
- getSourceCodeOfFiles(filename, {}, spy);
- process.chdir(originalDir);
- assert(spy.calledOnce);
- });
-
- it("should call the callback with total number of files being processed", () => {
- const filename = getFixturePath("foo.js");
- const spy = sinon.spy();
-
- process.chdir(fixtureDir);
- getSourceCodeOfFiles(filename, {}, spy);
- process.chdir(originalDir);
- assert.strictEqual(spy.firstCall.args[0], 1);
- });
-
- it("should create an object with located filenames as keys", () => {
- const fooFilename = getFixturePath("foo.js");
- const barFilename = getFixturePath("bar.js");
- const sourceCode = getSourceCodeOfFiles([fooFilename, barFilename], { cwd: fixtureDir });
-
- assert.property(sourceCode, fooFilename);
- assert.property(sourceCode, barFilename);
- });
-
- it("should should not include non-existent filenames in results", () => {
- const filename = getFixturePath("missing.js");
-
- assert.throws(() => {
- getSourceCodeOfFiles(filename, { cwd: fixtureDir });
- }, `No files matching '${filename}' were found.`);
- });
-
- it("should throw for files with parsing errors", () => {
- const filename = getFixturePath("parse-error", "parse-error.js");
-
- assert.throw(() => {
- getSourceCodeOfFiles(filename, { cwd: fixtureDir });
- }, /Parsing error: Unexpected token ;/u);
-
- });
-
- it("should obtain the sourceCode of a file", () => {
- const filename = getFixturePath("foo.js");
- const sourceCode = getSourceCodeOfFiles(filename, { cwd: fixtureDir });
-
- assert.isObject(sourceCode);
- assert.instanceOf(sourceCode[filename], SourceCode);
- });
-
- it("should obtain the sourceCode of JSX files", () => {
- const filename = getFixturePath("jsx", "foo.jsx");
- const options = {
- cwd: fixtureDir,
- parserOptions: {
- ecmaFeatures: {
- jsx: true
- }
- }
- };
- const sourceCode = getSourceCodeOfFiles(filename, options);
-
- assert.isObject(sourceCode);
- assert.instanceOf(sourceCode[filename], SourceCode);
- });
-
- it("should honor .eslintignore files by default", () => {
- const glob = getFixturePath("*.js");
- const unignoredFilename = getFixturePath("foo.js");
- const ignoredFilename = getFixturePath("ignored.js");
- const sourceCode = getSourceCodeOfFiles(glob, { cwd: fixtureDir });
-
- assert.property(sourceCode, unignoredFilename);
- assert.notProperty(sourceCode, ignoredFilename);
- });
-
- it("should obtain the sourceCode of all files in a specified folder", () => {
- const folder = getFixturePath("nested");
- const fooFile = getFixturePath("nested/foo.js");
- const barFile = getFixturePath("nested/bar.js");
- const sourceCode = getSourceCodeOfFiles(folder, { cwd: fixtureDir });
-
- assert.strictEqual(Object.keys(sourceCode).length, 2);
- assert.instanceOf(sourceCode[fooFile], SourceCode);
- assert.instanceOf(sourceCode[barFile], SourceCode);
- });
-
- it("should accept cli options", () => {
- const pattern = getFixturePath("ext");
- const abcFile = getFixturePath("ext/foo.abc");
- const cliOptions = { extensions: [".abc"], cwd: fixtureDir };
- const sourceCode = getSourceCodeOfFiles(pattern, cliOptions);
-
- assert.strictEqual(Object.keys(sourceCode).length, 1);
- assert.instanceOf(sourceCode[abcFile], SourceCode);
- });
-
- it("should execute the callback function, if provided", () => {
- const callback = sinon.spy();
- const filename = getFixturePath("foo.js");
-
- getSourceCodeOfFiles(filename, { cwd: fixtureDir }, callback);
- assert(callback.calledOnce);
- });
-
- it("should execute callback function once per file", () => {
- const callback = sinon.spy();
- const fooFilename = getFixturePath("foo.js");
- const barFilename = getFixturePath("bar.js");
-
- getSourceCodeOfFiles([fooFilename, barFilename], { cwd: fixtureDir }, callback);
- assert.strictEqual(callback.callCount, 2);
- });
-
- it("should call callback function with total number of files with sourceCode", () => {
- const callback = sinon.spy();
- const firstFn = getFixturePath("foo.js");
- const secondFn = getFixturePath("bar.js");
- const thirdFn = getFixturePath("nested/foo.js");
-
- getSourceCodeOfFiles([firstFn, secondFn, thirdFn], { cwd: fixtureDir }, callback);
- assert(callback.calledWith(3));
- });
-
- });
-
-});
diff --git a/tests/tools/eslint-fuzzer.js b/tests/tools/eslint-fuzzer.js
index 4723befa4e28..1c894b99595b 100644
--- a/tests/tools/eslint-fuzzer.js
+++ b/tests/tools/eslint-fuzzer.js
@@ -8,7 +8,7 @@ const assert = require("chai").assert;
const eslint = require("../..");
const espree = require("espree");
const sinon = require("sinon");
-const configRule = require("../../lib/init/config-rule");
+const configRule = require("../../tools/config-rule");
//------------------------------------------------------------------------------
// Tests
diff --git a/lib/init/config-rule.js b/tools/config-rule.js
similarity index 99%
rename from lib/init/config-rule.js
rename to tools/config-rule.js
index 131e84a60c5c..91e7eaef5a41 100644
--- a/lib/init/config-rule.js
+++ b/tools/config-rule.js
@@ -9,7 +9,7 @@
// Requirements
//------------------------------------------------------------------------------
-const builtInRules = require("../rules");
+const builtInRules = require("../lib/rules");
//------------------------------------------------------------------------------
// Helpers
diff --git a/tools/eslint-fuzzer.js b/tools/eslint-fuzzer.js
index 6c5ed9b1bcb4..d951ad3377c1 100644
--- a/tools/eslint-fuzzer.js
+++ b/tools/eslint-fuzzer.js
@@ -13,7 +13,7 @@ const assert = require("assert");
const eslump = require("eslump");
const espree = require("espree");
const SourceCodeFixer = require("../lib/linter/source-code-fixer");
-const ruleConfigs = require("../lib/init/config-rule").createCoreRuleConfigs(true);
+const ruleConfigs = require("./config-rule").createCoreRuleConfigs(true);
const sampleMinimizer = require("./code-sample-minimizer");
//------------------------------------------------------------------------------