You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
7.8 KiB

/**
* @fileoverview Config initialization wizard.
* @author Ilya Volodin
* @copyright 2015 Ilya Volodin. All rights reserved.
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
var exec = require("child_process").exec,
inquirer = require("inquirer"),
ConfigFile = require("./config-file");
//------------------------------------------------------------------------------
// 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.
* @param {function} callback function to call once the file is written.
* @returns {void}
*/
function writeFile(config, format, callback) {
// default is .js
var extname = ".js";
if (format === "YAML") {
extname = ".yml";
} else if (format === "JSON") {
extname = ".json";
}
try {
ConfigFile.write(config, "./.eslintrc" + extname);
console.log("Successfully created .eslintrc" + extname + " file in " + process.cwd());
} catch (e) {
callback(e);
return;
}
// install any external configs as well as any included plugins
if (config.extends && config.extends.indexOf("eslint") === -1) {
console.log("Installing additional dependencies");
exec("npm i eslint-config-" + config.extends + " --save-dev", function(err) {
if (err) {
return callback(err);
}
// TODO: consider supporting more than 1 plugin though it's required yet.
exec("npm i eslint-plugin-" + config.plugins[0] + " --save-dev", callback);
});
return;
}
// install the react plugin if it was explictly chosen
if (config.plugins && config.plugins.indexOf("react") >= 0) {
console.log("Installing React plugin");
exec("npm i eslint-plugin-react --save-dev", callback);
return;
}
callback();
}
/**
* process user's answers and create config object
* @param {object} answers answers received from inquirer
* @returns {object} config object
*/
function processAnswers(answers) {
var config = {rules: {}, env: {}, extends: "eslint:recommended"};
config.rules.indent = [2, answers.indent];
config.rules.quotes = [2, answers.quotes];
config.rules["linebreak-style"] = [2, answers.linebreak];
config.rules.semi = [2, answers.semi ? "always" : "never"];
if (answers.es6) {
config.env.es6 = true;
}
answers.env.forEach(function(env) {
config.env[env] = true;
});
if (answers.jsx) {
config.ecmaFeatures = {jsx: true};
if (answers.react) {
config.plugins = ["react"];
config.ecmaFeatures.experimentalObjectRestSpread = true;
}
}
return config;
}
/**
* process user's style guide of choice and return an appropriate config object.
* @param {string} guide name of the chosen style guide
* @returns {object} config object
*/
function getConfigForStyleGuide(guide) {
var guides = {
google: {extends: "google"},
airbnb: {extends: "airbnb", plugins: ["react"]},
standard: {extends: "standard", plugins: ["standard"]}
};
if (!guides[guide]) {
throw new Error("You referenced an unsupported guide.");
}
return guides[guide];
}
/* istanbul ignore next: no need to test inquirer*/
/**
* Ask use a few questions on command prompt
* @param {function} callback callback function when file has been written
* @returns {void}
*/
function promptUser(callback) {
inquirer.prompt([
{
type: "list",
name: "source",
message: "How would you like to configure ESLint?",
default: "prompt",
choices: [{name: "Answer questions about your style", value: "prompt"}, {name: "Use a popular style guide", value: "guide"}]
},
{
type: "list",
name: "styleguide",
message: "Which style guide do you want to follow?",
choices: [{name: "Google", value: "google"}, {name: "AirBnB", value: "airbnb"}, {name: "Standard", value: "standard"}],
when: function(answers) {
return answers.source === "guide";
}
},
{
type: "list",
name: "format",
message: "What format do you want your config file to be in?",
default: "JavaScript",
choices: ["JavaScript", "YAML", "JSON"],
when: function(answers) {
return answers.source === "guide";
}
}
], function(earlyAnswers) {
// early exit if you are using a style guide
if (earlyAnswers.source === "guide") {
writeFile(getConfigForStyleGuide(earlyAnswers.styleguide), earlyAnswers.format, callback);
return;
}
// continue with the style questions otherwise...
inquirer.prompt([
{
type: "list",
name: "indent",
message: "What style of indentation do you use?",
default: "tabs",
choices: [{name: "Tabs", value: "tab"}, {name: "Spaces", value: 4}]
},
{
type: "list",
name: "quotes",
message: "What quotes do you use for strings?",
default: "double",
choices: [{name: "Double", value: "double"}, {name: "Single", value: "single"}]
},
{
type: "list",
name: "linebreak",
message: "What line endings do you use?",
default: "unix",
choices: [{name: "Unix", value: "unix"}, {name: "Windows", value: "windows"}]
},
{
type: "confirm",
name: "semi",
message: "Do you require semicolons?",
default: true
},
{
type: "confirm",
name: "es6",
message: "Are you using ECMAScript 6 features?",
default: false
},
{
type: "checkbox",
name: "env",
message: "Where will your code run?",
default: ["browser"],
choices: [{name: "Node", value: "node"}, {name: "Browser", value: "browser"}]
},
{
type: "confirm",
name: "jsx",
message: "Do you use JSX?",
default: false
},
{
type: "confirm",
name: "react",
message: "Do you use React",
default: false,
when: function(answers) {
return answers.jsx;
}
},
{
type: "list",
name: "format",
message: "What format do you want your config file to be in?",
default: "JavaScript",
choices: ["JavaScript", "YAML", "JSON"]
}
], function(answers) {
var config = processAnswers(answers);
writeFile(config, answers.format, callback);
});
});
}
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
var init = {
getConfigForStyleGuide: getConfigForStyleGuide,
processAnswers: processAnswers,
initializeConfig: /* istanbul ignore next */ function(callback) {
promptUser(callback);
}
};
module.exports = init;