Repository: eslint/eslint (js)

Issue:
Rule change request: indent: allow "off" for "outerIIFEBody"
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for requesting a rule change. If you are here for another reason, please see below:

    1. To report a bug: https://eslint.org/docs/developer-guide/contributing/reporting-bugs
    2. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
    3. To request a change that is not a bug fix, rule change, or new rule: https://eslint.org/docs/developer-guide/contributing/changes
    4. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**What rule do you want to change?** [indent](https://eslint.org/docs/rules/indent)

**Does this change cause the rule to produce more or fewer warnings?** Fewer warnings when applied (but the default behavior would be the same)

**How will the change be implemented? (New option, new default behavior, etc.)?** New option, default behavior unchanged

Several options for the `indent` rule accept the string `"off"` to disable the check and let the user do it however they want. But a few of them do not, such as `"outerIIFEBody"`. I would like to be able to set `"outerIIFEBody"` to `"off"`. Currently I have to choose between 0 or 1 but both choices enforce something, and I don't want to enforce anything.

Also, I find it curious that several of the options support the `"off"` setting but only `"outerIIFEBody"` and a few others don't.

**Please provide some example code that this change will affect:**

<!-- Put your code examples here -->
```js
// File 1
(function() {
    a();
)();

// File 2
(function() {
b();
)();
```

**What does the rule currently do for this code?** One of the files will cause a warning, depending whether I choose 0 or 1 for the `"outerIIFEBody"` rule.

**What will the rule do after it's changed?** With the `"off"` option, eslint would not give a warning for any of those cases, while still checking the other indentation rules for the other options.

**Are you willing to submit a pull request to implement this change?** Sadly I don't have time for this at the moment...


Pull request description:
Update: add outerIIFEBody: "off" to indent rule (fixes #11377)
<!--
    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)**

[ ] 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))
[X] 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:
-->
fixes #11377 

**What changes did you make? (Give an overview)**
This PR adds the ability to set the `outerIIFEBody`  to `"off"`. This was a lot easier than I expected - the logic already allows for this, so I just needed to add the option to the JSON schema. 

**Is there anything you'd like reviewers to focus on?**
Nothing in particular!


Patch:
diff --git a/docs/rules/indent.md b/docs/rules/indent.md
index 8d7f9d059..7c2e6b7a6 100644
--- a/docs/rules/indent.md
+++ b/docs/rules/indent.md
@@ -70,7 +70,7 @@ This rule has an object option:
 
 * `"SwitchCase"` (default: 0) enforces indentation level for `case` clauses in `switch` statements
 * `"VariableDeclarator"` (default: 1) enforces indentation level for `var` declarators; can also take an object to define separate rules for `var`, `let` and `const` declarations. It can also be `"first"`, indicating all the declarators should be aligned with the first declarator.
-* `"outerIIFEBody"` (default: 1) enforces indentation level for file-level IIFEs.
+* `"outerIIFEBody"` (default: 1) enforces indentation level for file-level IIFEs. This can also be set to `"off"` to disable checking for file-level IIFEs.
 * `"MemberExpression"` (default: 1) enforces indentation level for multi-line property chains. This can also be set to `"off"` to disable checking for MemberExpression indentation.
 * `"FunctionDeclaration"` takes an object to define rules for function declarations.
     * `parameters` (default: 1) enforces indentation level for parameters in a function declaration. This can either be a number indicating indentation level, or the string `"first"` indicating that all parameters of the declaration must be aligned with the first parameter. This can also be set to `"off"` to disable checking for FunctionDeclaration parameters.
@@ -281,12 +281,12 @@ Examples of **incorrect** code for this rule with the options `2, { "outerIIFEBo
 })();
 
 
-if(y) {
+if (y) {
 console.log('foo');
 }
 ```
 
-Examples of **correct** code for this rule with the options `2, {"outerIIFEBody": 0}`:
+Examples of **correct** code for this rule with the options `2, { "outerIIFEBody": 0 }`:
 
 ```js
 /*eslint indent: ["error", 2, { "outerIIFEBody": 0 }]*/
@@ -300,11 +300,37 @@ function foo(x) {
 })();
 
 
-if(y) {
+if (y) {
    console.log('foo');
 }
 ```
 
+Examples of **correct** code for this rule with the options `2, { "outerIIFEBody":  "off" }`:
+
+```js
+/*eslint indent: ["error", 2, { "outerIIFEBody": "off" }]*/
+
+(function() {
+
+function foo(x) {
+  return x + 1;
+}
+
+})();
+
+(function() {
+
+  function foo(x) {
+    return x + 1;
+  }
+
+})();
+
+if (y) {
+  console.log('foo');
+}
+```
+
 ### MemberExpression
 
 Examples of **incorrect** code for this rule with the `2, { "MemberExpression": 1 }` options:
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index 9fa62c84a..59d50e2cc 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -540,8 +540,15 @@ module.exports = {
                         ]
                     },
                     outerIIFEBody: {
-                        type: "integer",
-                        minimum: 0
+                        oneOf: [
+                            {
+                                type: "integer",
+                                minimum: 0
+                            },
+                            {
+                                enum: ["off"]
+                            }
+                        ]
                     },
                     MemberExpression: {
                         oneOf: [
@@ -1092,7 +1099,6 @@ module.exports = {
             },
 
             "BlockStatement, ClassBody"(node) {
-
                 let blockIndentLevel;
 
                 if (node.parent && isOuterIIFE(node.parent)) {
@@ -1112,6 +1118,7 @@ module.exports = {
                 if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
                     offsets.setDesiredOffset(sourceCode.getFirstToken(node), sourceCode.getFirstToken(node.parent), 0);
                 }
+
                 addElementListIndent(node.body, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), blockIndentLevel);
             },
 


Test patch:
diff --git a/lib/rule-tester/rule-tester.js b/lib/rule-tester/rule-tester.js
index 44e01dadf..4b8c38692 100644
--- a/lib/rule-tester/rule-tester.js
+++ b/lib/rule-tester/rule-tester.js
@@ -50,6 +50,52 @@ const
 
 const ajv = require("../shared/ajv")({ strictDefaults: true });
 
+
+//------------------------------------------------------------------------------
+// Typedefs
+//------------------------------------------------------------------------------
+
+/**
+ * A test case that is expected to pass lint.
+ * @typedef {Object} ValidTestCase
+ * @property {string} code Code for the test case.
+ * @property {any[]} [options] Options for the test case.
+ * @property {{ [name: string]: any }} [settings] Settings for the test case.
+ * @property {string} [filename] The fake filename for the test case. Useful for rules that make assertion about filenames.
+ * @property {string} [parser] The absolute path for the parser.
+ * @property {{ [name: string]: any }} [parserOptions] Options for the parser.
+ * @property {{ [name: string]: "readonly" | "writable" | "off" }} [globals] The additional global variables.
+ * @property {{ [name: string]: boolean }} [env] Environments for the test case.
+ */
+
+/**
+ * A test case that is expected to fail lint.
+ * @typedef {Object} InvalidTestCase
+ * @property {string} code Code for the test case.
+ * @property {number | Array<TestCaseError | string | RegExp>} errors Expected errors.
+ * @property {string | null} [output] The expected code after autofixes are applied. If set to `null`, the test runner will assert that no autofix is suggested.
+ * @property {any[]} [options] Options for the test case.
+ * @property {{ [name: string]: any }} [settings] Settings for the test case.
+ * @property {string} [filename] The fake filename for the test case. Useful for rules that make assertion about filenames.
+ * @property {string} [parser] The absolute path for the parser.
+ * @property {{ [name: string]: any }} [parserOptions] Options for the parser.
+ * @property {{ [name: string]: "readonly" | "writable" | "off" }} [globals] The additional global variables.
+ * @property {{ [name: string]: boolean }} [env] Environments for the test case.
+ */
+
+/**
+ * A description of a reported error used in a rule tester test.
+ * @typedef {Object} TestCaseError
+ * @property {string | RegExp} [message] Message.
+ * @property {string} [messageId] Message ID.
+ * @property {string} [type] The type of the reported AST node.
+ * @property {{ [name: string]: string }} [data] The data used to fill the message template.
+ * @property {number} [line] The 1-based line number of the reported start location.
+ * @property {number} [column] The 1-based column number of the reported start location.
+ * @property {number} [endLine] The 1-based line number of the reported end location.
+ * @property {number} [endColumn] The 1-based column number of the reported end location.
+ */
+
 //------------------------------------------------------------------------------
 // Private Members
 //------------------------------------------------------------------------------
@@ -273,7 +319,10 @@ class RuleTester {
      * Adds a new rule test to execute.
      * @param {string} ruleName The name of the rule to run.
      * @param {Function} rule The rule to test.
-     * @param {Object} test The collection of tests to run.
+     * @param {{
+     *   valid: (ValidTestCase | string)[],
+     *   invalid: InvalidTestCase[]
+     * }} test The collection of tests to run.
      * @returns {void}
      */
     run(ruleName, rule, test) {
diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js
index 93ebb2824..99e7dfa02 100644
--- a/tests/lib/rules/indent.js
+++ b/tests/lib/rules/indent.js
@@ -1787,6 +1787,49 @@ ruleTester.run("indent", rule, {
             `,
             options: [2, { outerIIFEBody: 0 }]
         },
+        {
+            code: unIndent`
+                (function(x) {
+                    return x + 1;
+                })();
+            `,
+            options: [4, { outerIIFEBody: "off" }]
+        },
+        {
+            code: unIndent`
+                (function(x) {
+                return x + 1;
+                })();
+            `,
+            options: [4, { outerIIFEBody: "off" }]
+        },
+        {
+            code: unIndent`
+                ;(() => {
+                    function x(y) {
+                        return y + 1;
+                    }
+                })();
+            `,
+            options: [4, { outerIIFEBody: "off" }]
+        },
+        {
+            code: unIndent`
+                ;(() => {
+                function x(y) {
+                    return y + 1;
+                }
+                })();
+            `,
+            options: [4, { outerIIFEBody: "off" }]
+        },
+        {
+            code: unIndent`
+                function foo() {
+                }
+            `,
+            options: [4, { outerIIFEBody: "off" }]
+        },
         {
             code: "Buffer.length",
             options: [4, { MemberExpression: 1 }]
@@ -6986,6 +7029,78 @@ ruleTester.run("indent", rule, {
             options: ["tab", { outerIIFEBody: 3 }],
             errors: expectedErrors("tab", [[3, 2, 4, "Keyword"]])
         },
+        {
+            code: unIndent`
+                (function(){
+                    function foo(x) {
+                    return x + 1;
+                    }
+                })();
+            `,
+            output: unIndent`
+                (function(){
+                    function foo(x) {
+                        return x + 1;
+                    }
+                })();
+            `,
+            options: [4, { outerIIFEBody: "off" }],
+            errors: expectedErrors([[3, 8, 4, "Keyword"]])
+        },
+        {
+            code: unIndent`
+                (function(){
+                function foo(x) {
+                return x + 1;
+                }
+                })();
+            `,
+            output: unIndent`
+                (function(){
+                function foo(x) {
+                    return x + 1;
+                }
+                })();
+            `,
+            options: [4, { outerIIFEBody: "off" }],
+            errors: expectedErrors([[3, 4, 0, "Keyword"]])
+        },
+        {
+            code: unIndent`
+                (() => {
+                    function foo(x) {
+                    return x + 1;
+                    }
+                })();
+            `,
+            output: unIndent`
+                (() => {
+                    function foo(x) {
+                        return x + 1;
+                    }
+                })();
+            `,
+            options: [4, { outerIIFEBody: "off" }],
+            errors: expectedErrors([[3, 8, 4, "Keyword"]])
+        },
+        {
+            code: unIndent`
+                (() => {
+                function foo(x) {
+                return x + 1;
+                }
+                })();
+            `,
+            output: unIndent`
+                (() => {
+                function foo(x) {
+                    return x + 1;
+                }
+                })();
+            `,
+            options: [4, { outerIIFEBody: "off" }],
+            errors: expectedErrors([[3, 4, 0, "Keyword"]])
+        },
         {
             code: unIndent`
                 Buffer
diff --git a/tests/lib/rules/one-var.js b/tests/lib/rules/one-var.js
index e8937d637..83fe567b7 100644
--- a/tests/lib/rules/one-var.js
+++ b/tests/lib/rules/one-var.js
@@ -201,13 +201,11 @@ ruleTester.run("one-var", rule, {
         },
         {
             code: "var foo = require('foo'), bar;",
-            options: [{ separateRequires: false, var: "always" }],
-            parserOptions: { env: { node: true } }
+            options: [{ separateRequires: false, var: "always" }]
         },
         {
             code: "var foo = require('foo'), bar = require('bar');",
-            options: [{ separateRequires: true, var: "always" }],
-            parserOptions: { env: { node: true } }
+            options: [{ separateRequires: true, var: "always" }]
         },
         {
             code: "var bar = 'bar'; var foo = require('foo');",
@@ -217,11 +215,6 @@ ruleTester.run("one-var", rule, {
             code: "var foo = require('foo'); var bar = 'bar';",
             options: [{ separateRequires: true, var: "always" }]
         },
-        {
-            code: "var foo = require('foo'); var bar = 'bar';",
-            options: [{ separateRequires: true, var: "always" }],
-            parserOptions: { env: { node: true } }
-        },
 
         // https://github.com/eslint/eslint/issues/4680
         {
@@ -968,7 +961,6 @@ ruleTester.run("one-var", rule, {
             code: "var foo = require('foo'), bar;",
             output: null,
             options: [{ separateRequires: true, var: "always" }],
-            parserOptions: { env: { node: true } },
             errors: [{
                 message: "Split requires to be separated into a single block.",
                 type: "VariableDeclaration",
@@ -980,7 +972,6 @@ ruleTester.run("one-var", rule, {
             code: "var foo, bar = require('bar');",
             output: null,
             options: [{ separateRequires: true, var: "always" }],
-            parserOptions: { env: { node: true } },
             errors: [{
                 message: "Split requires to be separated into a single block.",
                 type: "VariableDeclaration",
@@ -992,7 +983,6 @@ ruleTester.run("one-var", rule, {
             code: "let foo, bar = require('bar');",
             output: null,
             options: [{ separateRequires: true, let: "always" }],
-            parserOptions: { env: { node: true } },
             errors: [{
                 message: "Split requires to be separated into a single block.",
                 type: "VariableDeclaration",
@@ -1004,7 +994,6 @@ ruleTester.run("one-var", rule, {
             code: "const foo = 0, bar = require('bar');",
             output: null,
             options: [{ separateRequires: true, const: "always" }],
-            parserOptions: { env: { node: true } },
             errors: [{
                 message: "Split requires to be separated into a single block.",
                 type: "VariableDeclaration",
@@ -1016,7 +1005,6 @@ ruleTester.run("one-var", rule, {
             code: "const foo = require('foo'); const bar = require('bar');",
             output: "const foo = require('foo'),  bar = require('bar');",
             options: [{ separateRequires: true, const: "always" }],
-            parserOptions: { env: { node: true } },
             errors: [{
                 message: "Combine this with the previous 'const' statement.",
                 type: "VariableDeclaration",