Repository: eslint/eslint (js)

Issue:
prefer-object-spread should discuss spec availability
the [prefer-spread](https://eslint.org/docs/rules/prefer-spread) page helpfully discusses the fact that it's new to ES2015, but there is no such info on the [prefer-object-spread](https://eslint.org/docs/rules/prefer-object-spread) page which makes it read like people should be able to readily use it.

unfortunately, it's new to [ES2018](https://github.com/tc39/proposal-object-rest-spread), and even then, going by the [MDN page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals), Edge & Safari don't yet support it.

Pull request description:
Docs: Add note about support for object spread (fixes #11136)
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[x] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**

Added a few notes to `prefer-object-spread` rule to indicate that it is only available in environments that support ES2018 features. 

**Is there anything you'd like reviewers to focus on?**
I tried to keep the scope of the change small - this is my first contribution to ESLint so lmk if i missed anything. Thank you



Patch:
diff --git a/README.md b/README.md
index 5c4267dfe..e0ee6a74f 100644
--- a/README.md
+++ b/README.md
@@ -266,7 +266,8 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <!-- NOTE: This section is autogenerated. Do not manually edit.-->
 <!--sponsorsstart-->
 <h3>Gold Sponsors</h3>
-<p><a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F098e3bd0-4d57-11e8-9324-0f6cc1f92bf1.png&height=96" alt="Airbnb" height="96"></a></p><h3>Bronze Sponsors</h3>
+<p><a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fres.cloudinary.com%2Fopencollective%2Fimage%2Fupload%2Fv1508519428%2FS9gk78AS_400x400_fulq2l.jpg&height=96" alt="Facebook Open Source" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F098e3bd0-4d57-11e8-9324-0f6cc1f92bf1.png&height=96" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
+<p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Fopencollective-production.s3-us-west-1.amazonaws.com%2F68ed8b70-ebf2-11e6-9958-cb7e79408c56.png&height=96" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
 <p><a href="http://faithlife.com/ref/about"><img src="https://images.opencollective.com/proxy/images/?src=https%3A%2F%2Flogo.clearbit.com%2Ffaithlife.com&height=96" alt="Faithlife" height="32"></a></p>
 <!--sponsorsend-->
 
diff --git a/docs/rules/prefer-object-spread.md b/docs/rules/prefer-object-spread.md
index b50e39716..95a646068 100644
--- a/docs/rules/prefer-object-spread.md
+++ b/docs/rules/prefer-object-spread.md
@@ -2,7 +2,7 @@
 
 When Object.assign is called using an object literal as the first argument, this rule requires using the object spread syntax instead. This rule also warns on cases where an `Object.assign` call is made using a single argument that is an object literal, in this case, the `Object.assign` call is not needed.
 
-Object spread is a declarative alternative which may perform better than the more dynamic, imperative `Object.assign`.
+Introduced in ES2018, object spread is a declarative alternative which may perform better than the more dynamic, imperative `Object.assign`.
 
 ## Rule Details
 
@@ -46,4 +46,4 @@ Object.assign(foo, { ...baz });
 
 ## When Not To Use It
 
-When object spread is not available in your codebase.
+This rule should not be used unless ES2018 is supported in your codebase.
diff --git a/lib/config/config-initializer.js b/lib/config/config-initializer.js
index a6791ba85..b8126d473 100644
--- a/lib/config/config-initializer.js
+++ b/lib/config/config-initializer.js
@@ -3,6 +3,7 @@
  * @author Ilya Volodin
  */
 
+
 "use strict";
 
 //------------------------------------------------------------------------------
@@ -28,6 +29,8 @@ const debug = require("debug")("eslint:config-initializer");
 // Private
 //------------------------------------------------------------------------------
 
+const DEFAULT_ECMA_VERSION = 2018;
+
 /* istanbul ignore next: hard to test fs function */
 /**
  * Create .eslintrc file in the current working directory
@@ -239,43 +242,65 @@ function configureRules(answers, config) {
  * @returns {Object} config object
  */
 function processAnswers(answers) {
-    let config = { rules: {}, env: {}, parserOptions: {} };
+    let config = {
+        rules: {},
+        env: {},
+        parserOptions: {},
+        extends: []
+    };
 
-    config.parserOptions.ecmaVersion = answers.ecmaVersion;
-    if (answers.ecmaVersion >= 2015) {
-        if (answers.modules) {
-            config.parserOptions.sourceType = "module";
-        }
-        config.env.es6 = true;
-    }
+    // set the latest ECMAScript version
+    config.parserOptions.ecmaVersion = DEFAULT_ECMA_VERSION;
+    config.env.es6 = true;
+    config.globals = {
+        Atomics: "readonly",
+        SharedArrayBuffer: "readonly"
+    };
 
-    if (answers.commonjs) {
+    // 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;
     });
-    if (answers.jsx) {
-        config.parserOptions = config.parserOptions || {};
-        config.parserOptions.ecmaFeatures = config.parserOptions.ecmaFeatures || {};
-        config.parserOptions.ecmaFeatures.jsx = true;
-        if (answers.react) {
-            config.plugins = ["react"];
-            config.parserOptions.ecmaVersion = 2018;
-        }
+
+    // add in library information
+    if (answers.framework === "react") {
+        config.parserOptions.ecmaFeatures = {
+            jsx: true
+        };
+        config.plugins = ["react"];
+    } else if (answers.framework === "vue") {
+        config.plugins = ["vue"];
+        config.extends.push("plugin:vue/essential");
     }
 
-    if (answers.source === "prompt") {
-        config.extends = "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"];
+    // 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.source === "auto") {
-        config = configureRules(answers, config);
-        config = autoconfig.extendFromRecommended(config);
+    // normalize extends
+    if (config.extends.length === 0) {
+        delete config.extends;
+    } else if (config.extends.length === 1) {
+        config.extends = config.extends[0];
     }
 
     ConfigOps.normalizeToStrings(config);
@@ -324,7 +349,7 @@ function getLocalESLintVersion() {
  * @returns {string} The shareable config name.
  */
 function getStyleGuideName(answers) {
-    if (answers.styleguide === "airbnb" && !answers.airbnbReact) {
+    if (answers.styleguide === "airbnb" && answers.framework !== "react") {
         return "airbnb-base";
     }
     return answers.styleguide;
@@ -417,16 +442,62 @@ function askInstallModules(modules, packageJsonExists) {
 function promptUser() {
 
     return inquirer.prompt([
+        {
+            type: "list",
+            name: "purpose",
+            message: "How would you like to use ESLint?",
+            default: "problems",
+            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" }
+            ]
+        },
+        {
+            type: "list",
+            name: "moduleType",
+            message: "What type of modules does your project use?",
+            default: "esm",
+            choices: [
+                { name: "JavaScript modules (import/export)", value: "esm" },
+                { name: "CommonJS (require/exports)", value: "commonjs" },
+                { name: "None of these", value: "none" }
+            ]
+        },
+        {
+            type: "list",
+            name: "framework",
+            message: "Which framework does your project use?",
+            default: "react",
+            choices: [
+                { name: "React", value: "react" },
+                { name: "Vue.js", value: "vue" },
+                { name: "None of these", value: "none" }
+            ]
+        },
+        {
+            type: "checkbox",
+            name: "env",
+            message: "Where does your code run?",
+            default: ["browser"],
+            choices: [
+                { name: "Browser", value: "browser" },
+                { name: "Node", value: "node" }
+            ]
+        },
         {
             type: "list",
             name: "source",
-            message: "How would you like to configure ESLint?",
-            default: "prompt",
+            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" }
-            ]
+            ],
+            when(answers) {
+                return answers.purpose === "style";
+            }
         },
         {
             type: "list",
@@ -442,15 +513,6 @@ function promptUser() {
                 return answers.source === "guide" && answers.packageJsonExists;
             }
         },
-        {
-            type: "confirm",
-            name: "airbnbReact",
-            message: "Do you use React?",
-            default: false,
-            when(answers) {
-                return answers.styleguide === "airbnb";
-            }
-        },
         {
             type: "input",
             name: "patterns",
@@ -470,10 +532,7 @@ function promptUser() {
             name: "format",
             message: "What format do you want your config file to be in?",
             default: "JavaScript",
-            choices: ["JavaScript", "YAML", "JSON"],
-            when(answers) {
-                return ((answers.source === "guide" && answers.packageJsonExists) || answers.source === "auto");
-            }
+            choices: ["JavaScript", "YAML", "JSON"]
         },
         {
             type: "confirm",
@@ -492,6 +551,15 @@ function promptUser() {
         }
     ]).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) {
@@ -501,130 +569,69 @@ function promptUser() {
             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.airbnbReact) {
+            if (earlyAnswers.styleguide === "airbnb" && earlyAnswers.framework !== "react") {
                 earlyAnswers.styleguide = "airbnb-base";
             }
 
-            const config = getConfigForStyleGuide(earlyAnswers.styleguide);
+            const config = ConfigOps.merge(processAnswers(earlyAnswers), getConfigForStyleGuide(earlyAnswers.styleguide));
             const modules = getModulesList(config);
 
             return askInstallModules(modules, earlyAnswers.packageJsonExists)
                 .then(() => writeFile(config, earlyAnswers.format));
+
         }
 
-        // continue with the questions otherwise...
+        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 inquirer.prompt([
             {
                 type: "list",
-                name: "ecmaVersion",
-                message: "Which version of ECMAScript do you use?",
-                choices: [
-                    { name: "ES3", value: 3 },
-                    { name: "ES5", value: 5 },
-                    { name: "ES2015", value: 2015 },
-                    { name: "ES2016", value: 2016 },
-                    { name: "ES2017", value: 2017 },
-                    { name: "ES2018", value: 2018 }
-                ],
-                default: 1 // This is the index in the choices list
+                name: "indent",
+                message: "What style of indentation do you use?",
+                default: "tab",
+                choices: [{ name: "Tabs", value: "tab" }, { name: "Spaces", value: 4 }]
             },
             {
-                type: "confirm",
-                name: "modules",
-                message: "Are you using ES6 modules?",
-                default: false,
-                when(answers) {
-                    return answers.ecmaVersion >= 2015;
-                }
-            },
-            {
-                type: "checkbox",
-                name: "env",
-                message: "Where will your code run?",
-                default: ["browser"],
-                choices: [{ name: "Browser", value: "browser" }, { name: "Node", value: "node" }]
+                type: "list",
+                name: "quotes",
+                message: "What quotes do you use for strings?",
+                default: "double",
+                choices: [{ name: "Double", value: "double" }, { name: "Single", value: "single" }]
             },
             {
-                type: "confirm",
-                name: "commonjs",
-                message: "Do you use CommonJS?",
-                default: false,
-                when(answers) {
-                    return answers.env.some(env => env === "browser");
-                }
+                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: "jsx",
-                message: "Do you use JSX?",
-                default: false
+                name: "semi",
+                message: "Do you require semicolons?",
+                default: true
             },
             {
-                type: "confirm",
-                name: "react",
-                message: "Do you use React?",
-                default: false,
-                when(answers) {
-                    return answers.jsx;
-                }
-            }
-        ]).then(secondAnswers => {
-
-            // early exit if you are using automatic style generation
-            if (earlyAnswers.source === "auto") {
-                const combinedAnswers = Object.assign({}, earlyAnswers, secondAnswers);
-
-                const config = processAnswers(combinedAnswers);
-
-                const modules = getModulesList(config);
-
-                return askInstallModules(modules).then(() => writeFile(config, earlyAnswers.format));
+                type: "list",
+                name: "format",
+                message: "What format do you want your config file to be in?",
+                default: "JavaScript",
+                choices: ["JavaScript", "YAML", "JSON"]
             }
+        ]).then(answers => {
+            const totalAnswers = Object.assign({}, earlyAnswers, answers);
 
-            // continue with the style questions otherwise...
-            return inquirer.prompt([
-                {
-                    type: "list",
-                    name: "indent",
-                    message: "What style of indentation do you use?",
-                    default: "tab",
-                    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: "list",
-                    name: "format",
-                    message: "What format do you want your config file to be in?",
-                    default: "JavaScript",
-                    choices: ["JavaScript", "YAML", "JSON"]
-                }
-            ]).then(answers => {
-                const totalAnswers = Object.assign({}, earlyAnswers, secondAnswers, answers);
-
-                const config = processAnswers(totalAnswers);
-                const modules = getModulesList(config);
+            const config = processAnswers(totalAnswers);
+            const modules = getModulesList(config);
 
-                return askInstallModules(modules).then(() => writeFile(config, answers.format));
-            });
+            return askInstallModules(modules).then(() => writeFile(config, answers.format));
         });
     });
 }


Test patch:
diff --git a/tests/lib/config/config-initializer.js b/tests/lib/config/config-initializer.js
index 3767fe376..d78651b1b 100644
--- a/tests/lib/config/config-initializer.js
+++ b/tests/lib/config/config-initializer.js
@@ -117,20 +117,17 @@ describe("configInitializer", () => {
 
             beforeEach(() => {
                 answers = {
+                    purpose: "style",
                     source: "prompt",
                     extendDefault: true,
                     indent: 2,
                     quotes: "single",
                     linebreak: "unix",
                     semi: true,
-                    ecmaVersion: 2015,
-                    modules: true,
+                    moduleType: "esm",
                     es6Globals: true,
                     env: ["browser"],
-                    jsx: false,
-                    react: false,
-                    format: "JSON",
-                    commonjs: false
+                    format: "JSON"
                 };
             });
 
@@ -142,7 +139,9 @@ describe("configInitializer", () => {
                 assert.deepStrictEqual(config.rules["linebreak-style"], ["error", "unix"]);
                 assert.deepStrictEqual(config.rules.semi, ["error", "always"]);
                 assert.strictEqual(config.env.es6, true);
-                assert.strictEqual(config.parserOptions.ecmaVersion, 2015);
+                assert.strictEqual(config.globals.Atomics, "readonly");
+                assert.strictEqual(config.globals.SharedArrayBuffer, "readonly");
+                assert.strictEqual(config.parserOptions.ecmaVersion, 2018);
                 assert.strictEqual(config.parserOptions.sourceType, "module");
                 assert.strictEqual(config.env.browser, true);
                 assert.strictEqual(config.extends, "eslint:recommended");
@@ -155,16 +154,8 @@ describe("configInitializer", () => {
                 assert.deepStrictEqual(config.rules.semi, ["error", "never"]);
             });
 
-            it("should enable jsx flag", () => {
-                answers.jsx = true;
-                const config = init.processAnswers(answers);
-
-                assert.strictEqual(config.parserOptions.ecmaFeatures.jsx, true);
-            });
-
             it("should enable react plugin", () => {
-                answers.jsx = true;
-                answers.react = true;
+                answers.framework = "react";
                 const config = init.processAnswers(answers);
 
                 assert.strictEqual(config.parserOptions.ecmaFeatures.jsx, true);
@@ -172,12 +163,13 @@ describe("configInitializer", () => {
                 assert.deepStrictEqual(config.plugins, ["react"]);
             });
 
-            it("should not enable es6", () => {
-                answers.ecmaVersion = 5;
+            it("should enable vue plugin", () => {
+                answers.framework = "vue";
                 const config = init.processAnswers(answers);
 
-                assert.strictEqual(config.parserOptions.ecmaVersion, 5);
-                assert.isUndefined(config.env.es6);
+                assert.strictEqual(config.parserOptions.ecmaVersion, 2018);
+                assert.deepStrictEqual(config.plugins, ["vue"]);
+                assert.deepStrictEqual(config.extends, ["eslint:recommended", "plugin:vue/essential"]);
             });
 
             it("should extend eslint:recommended", () => {
@@ -193,7 +185,7 @@ describe("configInitializer", () => {
             });
 
             it("should use commonjs when set", () => {
-                answers.commonjs = true;
+                answers.moduleType = "commonjs";
                 const config = init.processAnswers(answers);
 
                 assert.isTrue(config.env.commonjs);
@@ -338,14 +330,11 @@ describe("configInitializer", () => {
                 ].join(" ");
 
                 answers = {
+                    purpose: "style",
                     source: "auto",
                     patterns,
-                    ecmaVersion: 5,
                     env: ["browser"],
-                    jsx: false,
-                    react: false,
-                    format: "JSON",
-                    commonjs: false
+                    format: "JSON"
                 };
 
                 sandbox = sinon.sandbox.create();