const util = require("util"),
path = require("path"),
- inquirer = require("inquirer"),
+ fs = require("fs"),
+ enquirer = require("enquirer"),
ProgressBar = require("progress"),
semver = require("semver"),
+ espree = require("espree"),
recConfig = require("../../conf/eslint-recommended"),
- ConfigOps = require("../shared/config-ops"),
+ ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
log = require("../shared/logging"),
- naming = require("../shared/naming"),
+ naming = require("@eslint/eslintrc/lib/shared/naming"),
ModuleResolver = require("../shared/relative-module-resolver"),
autoconfig = require("./autoconfig.js"),
ConfigFile = require("./config-file"),
// Private
//------------------------------------------------------------------------------
-const DEFAULT_ECMA_VERSION = 2018;
-
/* istanbul ignore next: hard to test fs function */
/**
* Create .eslintrc file in the current working directory
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;
*
* Note: This clones the config object and returns a new config to avoid mutating
* the original config parameter.
- * @param {Object} answers answers received from inquirer
+ * @param {Object} answers answers received from enquirer
* @param {Object} config config object
* @returns {Object} config object with configured rules
*/
/**
* process user's answers and create config object
- * @param {Object} answers answers received from inquirer
+ * @param {Object} answers answers received from enquirer
* @returns {Object} config object
*/
function processAnswers(answers) {
extends: []
};
- // set the latest ECMAScript version
- config.parserOptions.ecmaVersion = DEFAULT_ECMA_VERSION;
- config.env.es6 = true;
- config.globals = {
- Atomics: "readonly",
- SharedArrayBuffer: "readonly"
- };
+ config.parserOptions.ecmaVersion = espree.latestEcmaVersion;
+ config.env.es2021 = true;
// set the module type
if (answers.moduleType === "esm") {
}
}
if (answers.typescript && config.extends.includes("eslint:recommended")) {
- config.extends.push("plugin:@typescript-eslint/eslint-recommended");
+ config.extends.push("plugin:@typescript-eslint/recommended");
}
// normalize extends
const eslint = require(eslintPath);
return eslint.linter.version || null;
- } catch (_err) {
+ } catch {
return null;
}
}
npmUtils.installSyncSaveDev(modules);
}
-/* istanbul ignore next: no need to test inquirer */
+/* istanbul ignore next: no need to test enquirer */
/**
* Ask user to install modules.
* @param {string[]} modules Array of modules to be installed.
log.info("The config that you've selected requires the following dependencies:\n");
log.info(modules.join(" "));
- return inquirer.prompt([
+ return enquirer.prompt([
{
- type: "confirm",
+ type: "toggle",
name: "executeInstallation",
message: "Would you like to install them now with npm?",
- default: true,
- when() {
- return modules.length && packageJsonExists;
+ enabled: "Yes",
+ disabled: "No",
+ initial: 1,
+ skip() {
+ return !(modules.length && packageJsonExists);
+ },
+ result(input) {
+ return this.skipped ? null : input;
}
}
]).then(({ executeInstallation }) => {
});
}
-/* istanbul ignore next: no need to test inquirer */
+/* 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 inquirer.prompt([
+ return enquirer.prompt([
{
- type: "list",
+ type: "select",
name: "purpose",
message: "How would you like to use ESLint?",
- default: "problems",
+
+ // The returned number matches the name value of nth in the choices array.
+ initial: 1,
choices: [
- { name: "To check syntax only", value: "syntax" },
- { name: "To check syntax and find problems", value: "problems" },
- { name: "To check syntax, find problems, and enforce code style", value: "style" }
+ { 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: "list",
+ type: "select",
name: "moduleType",
message: "What type of modules does your project use?",
- default: "esm",
+ initial: 0,
choices: [
- { name: "JavaScript modules (import/export)", value: "esm" },
- { name: "CommonJS (require/exports)", value: "commonjs" },
- { name: "None of these", value: "none" }
+ { message: "JavaScript modules (import/export)", name: "esm" },
+ { message: "CommonJS (require/exports)", name: "commonjs" },
+ { message: "None of these", name: "none" }
]
},
{
- type: "list",
+ type: "select",
name: "framework",
message: "Which framework does your project use?",
- default: "react",
+ initial: 0,
choices: [
- { name: "React", value: "react" },
- { name: "Vue.js", value: "vue" },
- { name: "None of these", value: "none" }
+ { message: "React", name: "react" },
+ { message: "Vue.js", name: "vue" },
+ { message: "None of these", name: "none" }
]
},
{
- type: "confirm",
+ type: "toggle",
name: "typescript",
message: "Does your project use TypeScript?",
- default: false
+ enabled: "Yes",
+ disabled: "No",
+ initial: 0
},
{
- type: "checkbox",
+ type: "multiselect",
name: "env",
message: "Where does your code run?",
- default: ["browser"],
+ hint: "(Press <space> to select, <a> to toggle all, <i> to invert selection)",
+ initial: 0,
choices: [
- { name: "Browser", value: "browser" },
- { name: "Node", value: "node" }
+ { message: "Browser", name: "browser" },
+ { message: "Node", name: "node" }
]
},
{
- type: "list",
+ type: "select",
name: "source",
message: "How would you like to define a style for your project?",
- default: "guide",
choices: [
- { name: "Use a popular style guide", value: "guide" },
- { name: "Answer questions about your style", value: "prompt" },
- { name: "Inspect your JavaScript file(s)", value: "auto" }
+ { message: "Use a popular style guide", name: "guide" },
+ { message: "Answer questions about your style", name: "prompt" },
+ { message: "Inspect your JavaScript file(s)", name: "auto" }
],
- when(answers) {
- return answers.purpose === "style";
+ skip() {
+ return this.state.answers.purpose !== "style";
+ },
+ result(input) {
+ return this.skipped ? null : input;
}
},
{
- type: "list",
+ type: "select",
name: "styleguide",
message: "Which style guide do you want to follow?",
choices: [
- { name: "Airbnb: https://github.com/airbnb/javascript", value: "airbnb" },
- { name: "Standard: https://github.com/standard/standard", value: "standard" },
- { name: "Google: https://github.com/google/eslint-config-google", value: "google" }
+ { 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" }
],
- when(answers) {
- answers.packageJsonExists = npmUtils.checkPackageJson();
- return answers.source === "guide" && answers.packageJsonExists;
+ 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?",
- when(answers) {
- return (answers.source === "auto");
+ skip() {
+ return this.state.answers.source !== "auto";
},
validate(input) {
- if (input.trim().length === 0 && input.trim() !== ",") {
+ if (!this.skipped && input.trim().length === 0 && input.trim() !== ",") {
return "You must tell us what code to examine. Try again.";
}
return true;
}
},
{
- type: "list",
+ type: "select",
name: "format",
message: "What format do you want your config file to be in?",
- default: "JavaScript",
+ initial: 0,
choices: ["JavaScript", "YAML", "JSON"]
},
{
- type: "confirm",
+ type: "toggle",
name: "installESLint",
- message(answers) {
+ 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}?`;
},
- default: true,
- when(answers) {
- return answers.source === "guide" && answers.packageJsonExists && hasESLintVersionConflict(answers);
+ 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 => {
}
// continue with the style questions otherwise...
- return inquirer.prompt([
+ return enquirer.prompt([
{
- type: "list",
+ type: "select",
name: "indent",
message: "What style of indentation do you use?",
- default: "tab",
- choices: [{ name: "Tabs", value: "tab" }, { name: "Spaces", value: 4 }]
+ initial: 0,
+ choices: [{ message: "Tabs", name: "tab" }, { message: "Spaces", name: 4 }]
},
{
- type: "list",
+ type: "select",
name: "quotes",
message: "What quotes do you use for strings?",
- default: "double",
- choices: [{ name: "Double", value: "double" }, { name: "Single", value: "single" }]
+ initial: 0,
+ choices: [{ message: "Double", name: "double" }, { message: "Single", name: "single" }]
},
{
- type: "list",
+ type: "select",
name: "linebreak",
message: "What line endings do you use?",
- default: "unix",
- choices: [{ name: "Unix", value: "unix" }, { name: "Windows", value: "windows" }]
+ initial: 0,
+ choices: [{ message: "Unix", name: "unix" }, { message: "Windows", name: "windows" }]
},
{
- type: "confirm",
+ type: "toggle",
name: "semi",
message: "Do you require semicolons?",
- default: true
+ enabled: "Yes",
+ disabled: "No",
+ initial: 1
}
]).then(answers => {
const totalAnswers = Object.assign({}, earlyAnswers, answers);
hasESLintVersionConflict,
installModules,
processAnswers,
+ writeFile,
/* istanbul ignore next */initializeConfig() {
return promptUser();
}