Repository: eslint/eslint
Pull request #12519 (2019-11-01 19:56:13)

Update: deprecate sourceCode#isSpaceBetweenTokens()
<!--
    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))
[ ] 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
[X] 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 evaluate just the last commit. We should wait to merge this until after we land https://github.com/eslint/eslint/pull/12491. I'll rebase at that time. Adding the "do not merge" label until then.
<!--
    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)**
While working on https://github.com/eslint/eslint/pull/12491, noticed that `sourceCode#isSpaceBetweenTokens()` currently allows for nodes as well as tokens as arguments. I think that it would make sense to rename this method to isSpaceBetween() and leave the ability for it to check between nodes and tokens, as that's a useful behavior that is most likely being used by custom rules (since we use it in at least one of our core rules). We could deprecate isSpaceBetweenTokens() and leave it as an alias that calls through to isSpaceBetween() for the sake of backwards compatibility.

**Is there anything you'd like reviewers to focus on?**
Does this API make sense?



kaicataldo: ### TSC Summary

While fixing a bug in in `sourceCode#isSpaceBetweenTokens()`, I noticed that the current documentation is incorrect, also making the name of the method not quite correct.

### TSC Question

In addition to the bug fix/documentation corrections in #12491, I'd like to propose we deprecate `sourceCode#isSpaceBetweenTokens()` to in favor of `sourceCode#isSpaceBetween()`. This more accurately describes the behavior, as the method currently will check between nodes as well as tokens, and better matches the names of other `sourceCode` methods that check between nodes or tokens.

This PR currently deprecates `sourceCode#isSpaceBetweenTokens()` in a backwards compatible way, having it call through to the new method.
kaicataldo: This was accepted at [yesterday's TSC meeting](https://github.com/eslint/tsc-meetings/pull/154).
kaicataldo: This has been rebased and is ready for review.

Patch:
diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md
index ef6cbd634..c5d880804 100644
--- a/docs/developer-guide/working-with-rules.md
+++ b/docs/developer-guide/working-with-rules.md
@@ -392,7 +392,7 @@ Once you have an instance of `SourceCode`, you can use the methods on it to work
 * `getCommentsAfter(nodeOrToken)` - returns an array of comment tokens that occur directly after the given node or token.
 * `getCommentsInside(node)` - returns an array of all comment tokens inside a given node.
 * `getJSDocComment(node)` - returns the JSDoc comment for a given node or `null` if there is none.
-* `isSpaceBetweenTokens(first, second)` - returns true if there is a whitespace character between the two tokens.
+* `isSpaceBetween(nodeOrToken, nodeOrToken)` - returns true if there is a whitespace character between the two tokens or, if given a node, the last token of the first node and the first token of the second node.
 * `getFirstToken(node, skipOptions)` - returns the first token representing the given node.
 * `getFirstTokens(node, countOptions)` - returns the first `count` tokens representing the given node.
 * `getLastToken(node, skipOptions)` - returns the last token representing the given node.
@@ -447,6 +447,7 @@ Please note that the following methods have been deprecated and will be removed
 * `getComments()` - replaced by `getCommentsBefore()`, `getCommentsAfter()`, and `getCommentsInside()`
 * `getTokenOrCommentBefore()` - replaced by `getTokenBefore()` with the `{ includeComments: true }` option
 * `getTokenOrCommentAfter()` - replaced by `getTokenAfter()` with the `{ includeComments: true }` option
+* `isSpaceBetweenTokens()` - replaced by `isSpaceBetween()`
 
 ### Options Schemas
 
diff --git a/docs/rules/camelcase.md b/docs/rules/camelcase.md
index 1765f1852..5c4efcfe4 100644
--- a/docs/rules/camelcase.md
+++ b/docs/rules/camelcase.md
@@ -14,6 +14,8 @@ This rule has an object option:
 * `"properties": "never"` does not check property names
 * `"ignoreDestructuring": false` (default) enforces camelcase style for destructured identifiers
 * `"ignoreDestructuring": true` does not check destructured identifiers
+* `"ignoreImports": false` (default) enforces camelcase style for ES2015 imports
+* `"ignoreImports": true` does not check ES2015 imports (but still checks any use of the imports later in the code except function arguments)
 * `allow` (`string[]`) list of properties to accept. Accept regex.
 
 ### properties: "always"
@@ -152,6 +154,36 @@ var { category_id = 1 } = query;
 var { category_id: category_id } = query;
 ```
 
+### ignoreImports: false
+
+Examples of **incorrect** code for this rule with the default `{ "ignoreImports": false }` option:
+
+```js
+/*eslint camelcase: "error"*/
+
+import { snake_cased } from 'mod';
+```
+
+### ignoreImports: true
+
+Examples of **incorrect** code for this rule with the `{ "ignoreImports": true }` option:
+
+```js
+/*eslint camelcase: ["error", {ignoreImports: true}]*/
+
+import default_import from 'mod';
+
+import * as namespaced_import from 'mod';
+```
+
+Examples of **correct** code for this rule with the `{ "ignoreImports": true }` option:
+
+```js
+/*eslint camelcase: ["error", {ignoreImports: true}]*/
+
+import { snake_cased } from 'mod';
+```
+
 ## allow
 
 Examples of **correct** code for this rule with the `allow` option:
diff --git a/lib/rules/camelcase.js b/lib/rules/camelcase.js
index cdc16fab9..a06c7f940 100644
--- a/lib/rules/camelcase.js
+++ b/lib/rules/camelcase.js
@@ -28,6 +28,10 @@ module.exports = {
                         type: "boolean",
                         default: false
                     },
+                    ignoreImports: {
+                        type: "boolean",
+                        default: false
+                    },
                     properties: {
                         enum: ["always", "never"]
                     },
@@ -56,6 +60,7 @@ module.exports = {
         const options = context.options[0] || {};
         let properties = options.properties || "";
         const ignoreDestructuring = options.ignoreDestructuring;
+        const ignoreImports = options.ignoreImports;
         const allow = options.allow || [];
 
         if (properties !== "always" && properties !== "never") {
@@ -79,7 +84,7 @@ module.exports = {
         function isUnderscored(name) {
 
             // if there's an underscore, it might be A_CONSTANT, which is okay
-            return name.indexOf("_") > -1 && name !== name.toUpperCase();
+            return name.includes("_") && name !== name.toUpperCase();
         }
 
         /**
@@ -89,9 +94,9 @@ module.exports = {
          * @private
          */
         function isAllowed(name) {
-            return allow.findIndex(
+            return allow.some(
                 entry => name === entry || name.match(new RegExp(entry, "u"))
-            ) !== -1;
+            );
         }
 
         /**
@@ -127,7 +132,7 @@ module.exports = {
          * @private
          */
         function report(node) {
-            if (reported.indexOf(node) < 0) {
+            if (!reported.includes(node)) {
                 reported.push(node);
                 context.report({ node, messageId: "notCamelCase", data: { name: node.name } });
             }
@@ -209,10 +214,18 @@ module.exports = {
                     }
 
                 // Check if it's an import specifier
-                } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) {
+                } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].includes(node.parent.type)) {
+
+                    if (node.parent.type === "ImportSpecifier" && ignoreImports) {
+                        return;
+                    }
 
                     // Report only if the local imported identifier is underscored
-                    if (node.parent.local && node.parent.local.name === node.name && nameIsUnderscored) {
+                    if (
+                        node.parent.local &&
+                        node.parent.local.name === node.name &&
+                        nameIsUnderscored
+                    ) {
                         report(node);
                     }
 
diff --git a/lib/source-code/source-code.js b/lib/source-code/source-code.js
index 8a6690959..20b442f23 100644
--- a/lib/source-code/source-code.js
+++ b/lib/source-code/source-code.js
@@ -432,7 +432,7 @@ class SourceCode extends TokenStore {
      * any of the tokens found between the two given nodes or tokens.
      * @public
      */
-    isSpaceBetweenTokens(first, second) {
+    isSpaceBetween(first, second) {
         if (nodesOrTokensOverlap(first, second)) {
             return false;
         }
@@ -457,6 +457,20 @@ class SourceCode extends TokenStore {
         return false;
     }
 
+    /**
+     * Determines if two nodes or tokens have at least one whitespace character
+     * between them. Order does not matter. Returns false if the given nodes or
+     * tokens overlap.
+     * @param {...ASTNode|Token} args The nodes or tokens to check between.
+     * @returns {boolean} True if there is a whitespace character between
+     * any of the tokens found between the two given nodes or tokens.
+     * @deprecated in favor of isSpaceBetween().
+     * @public
+     */
+    isSpaceBetweenTokens(...args) {
+        return this.isSpaceBetween(...args);
+    }
+
     /**
      * Converts a source text index into a (line, column) pair.
      * @param {number} index The index of a character in a file
diff --git a/package.json b/package.json
index 26aed10db..ca8426f8d 100644
--- a/package.json
+++ b/package.json
@@ -74,7 +74,7 @@
     "minimatch": "^3.0.4",
     "mkdirp": "^0.5.1",
     "natural-compare": "^1.4.0",
-    "optionator": "^0.8.2",
+    "optionator": "^0.8.3",
     "progress": "^2.0.0",
     "regexpp": "^2.0.1",
     "semver": "^6.1.2",


Test patch:
diff --git a/tests/lib/rules/camelcase.js b/tests/lib/rules/camelcase.js
index 4aa77abed..1181561ef 100644
--- a/tests/lib/rules/camelcase.js
+++ b/tests/lib/rules/camelcase.js
@@ -151,6 +151,16 @@ ruleTester.run("camelcase", rule, {
             code: "import { no_camelcased as camelCased, anoterCamelCased } from \"external-module\";",
             parserOptions: { ecmaVersion: 6, sourceType: "module" }
         },
+        {
+            code: "import { snake_cased } from 'mod'",
+            options: [{ ignoreImports: true }],
+            parserOptions: { ecmaVersion: 6, sourceType: "module" }
+        },
+        {
+            code: "import { camelCased } from 'mod'",
+            options: [{ ignoreImports: false }],
+            parserOptions: { ecmaVersion: 6, sourceType: "module" }
+        },
         {
             code: "function foo({ no_camelcased: camelCased }) {};",
             parserOptions: { ecmaVersion: 6 }
@@ -532,6 +542,54 @@ ruleTester.run("camelcase", rule, {
                 }
             ]
         },
+        {
+            code: "import snake_cased from 'mod'",
+            options: [{ ignoreImports: true }],
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    messageId: "notCamelCase",
+                    data: { name: "snake_cased" },
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import * as snake_cased from 'mod'",
+            options: [{ ignoreImports: true }],
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    messageId: "notCamelCase",
+                    data: { name: "snake_cased" },
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import snake_cased from 'mod'",
+            options: [{ ignoreImports: false }],
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    messageId: "notCamelCase",
+                    data: { name: "snake_cased" },
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "import * as snake_cased from 'mod'",
+            options: [{ ignoreImports: false }],
+            parserOptions: { ecmaVersion: 6, sourceType: "module" },
+            errors: [
+                {
+                    messageId: "notCamelCase",
+                    data: { name: "snake_cased" },
+                    type: "Identifier"
+                }
+            ]
+        },
         {
             code: "function foo({ no_camelcased }) {};",
             parserOptions: { ecmaVersion: 6 },
diff --git a/tests/lib/source-code/source-code.js b/tests/lib/source-code/source-code.js
index d4d1dd4dd..29dbf1d6f 100644
--- a/tests/lib/source-code/source-code.js
+++ b/tests/lib/source-code/source-code.js
@@ -1789,6 +1789,339 @@ describe("SourceCode", () => {
         });
     });
 
+    describe("isSpaceBetween()", () => {
+        describe("should return true when there is at least one whitespace character between two tokens", () => {
+            leche.withData([
+                ["let foo", true],
+                ["let  foo", true],
+                ["let /**/ foo", true],
+                ["let/**/foo", false],
+                ["let/*\n*/foo", false]
+            ], (code, expected) => {
+                describe("when the first given is located before the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.tokens[0],
+                                sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1]
+                            ),
+                            expected
+                        );
+                    });
+                });
+
+                describe("when the first given is located after the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1],
+                                sourceCode.ast.tokens[0]
+                            ),
+                            expected
+                        );
+                    });
+                });
+            });
+
+            leche.withData([
+                ["a+b", false],
+                ["a +b", true],
+                ["a/**/+b", false],
+                ["a/* */+b", false],
+                ["a/**/ +b", true],
+                ["a/**/ /**/+b", true],
+                ["a/* */ /* */+b", true],
+                ["a/**/\n/**/+b", true],
+                ["a/* */\n/* */+b", true],
+                ["a/**/+b/**/+c", false],
+                ["a/* */+b/* */+c", false],
+                ["a/**/+b /**/+c", true],
+                ["a/* */+b /* */+c", true],
+                ["a/**/ +b/**/+c", true],
+                ["a/* */ +b/* */+c", true],
+                ["a/**/+b\t/**/+c", true],
+                ["a/* */+b\t/* */+c", true],
+                ["a/**/\t+b/**/+c", true],
+                ["a/* */\t+b/* */+c", true],
+                ["a/**/+b\n/**/+c", true],
+                ["a/* */+b\n/* */+c", true],
+                ["a/**/\n+b/**/+c", true],
+                ["a/* */\n+b/* */+c", true],
+                ["a/* */+' /**/ '/* */+c", false],
+                ["a/* */+ ' /**/ '/* */+c", true],
+                ["a/* */+' /**/ ' /* */+c", true],
+                ["a/* */+ ' /**/ ' /* */+c", true],
+                ["a/* */+` /*\n*/ `/* */+c", false],
+                ["a/* */+ ` /*\n*/ `/* */+c", true],
+                ["a/* */+` /*\n*/ ` /* */+c", true],
+                ["a/* */+ ` /*\n*/ ` /* */+c", true]
+            ], (code, expected) => {
+                describe("when the first given is located before the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.tokens[0],
+                                sourceCode.ast.tokens[sourceCode.ast.tokens.length - 2]
+                            ),
+                            expected
+                        );
+                    });
+                });
+
+                describe("when the first given is located after the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.tokens[sourceCode.ast.tokens.length - 2],
+                                sourceCode.ast.tokens[0]
+                            ),
+                            expected
+                        );
+                    });
+                });
+            });
+        });
+
+        describe("should return true when there is at least one whitespace character between a token and a node", () => {
+            leche.withData([
+                [";let foo = bar", false],
+                [";/**/let foo = bar", false],
+                [";/* */let foo = bar", false],
+                ["; let foo = bar", true],
+                ["; let foo = bar", true],
+                ["; /**/let foo = bar", true],
+                ["; /* */let foo = bar", true],
+                [";/**/ let foo = bar", true],
+                [";/* */ let foo = bar", true],
+                ["; /**/ let foo = bar", true],
+                ["; /* */ let foo = bar", true],
+                [";\tlet foo = bar", true],
+                [";\tlet foo = bar", true],
+                [";\t/**/let foo = bar", true],
+                [";\t/* */let foo = bar", true],
+                [";/**/\tlet foo = bar", true],
+                [";/* */\tlet foo = bar", true],
+                [";\t/**/\tlet foo = bar", true],
+                [";\t/* */\tlet foo = bar", true],
+                [";\nlet foo = bar", true],
+                [";\nlet foo = bar", true],
+                [";\n/**/let foo = bar", true],
+                [";\n/* */let foo = bar", true],
+                [";/**/\nlet foo = bar", true],
+                [";/* */\nlet foo = bar", true],
+                [";\n/**/\nlet foo = bar", true],
+                [";\n/* */\nlet foo = bar", true]
+            ], (code, expected) => {
+                describe("when the first given is located before the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.tokens[0],
+                                sourceCode.ast.body[sourceCode.ast.body.length - 1]
+                            ),
+                            expected
+                        );
+                    });
+                });
+
+                describe("when the first given is located after the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.body[sourceCode.ast.body.length - 1],
+                                sourceCode.ast.tokens[0]
+                            ),
+                            expected
+                        );
+                    });
+                });
+            });
+        });
+
+        describe("should return true when there is at least one whitespace character between a node and a token", () => {
+            leche.withData([
+                ["let foo = bar;;", false],
+                ["let foo = bar;;;", false],
+                ["let foo = 1; let bar = 2;;", true],
+                ["let foo = bar;/**/;", false],
+                ["let foo = bar;/* */;", false],
+                ["let foo = bar;;;", false],
+                ["let foo = bar; ;", true],
+                ["let foo = bar; /**/;", true],
+                ["let foo = bar; /* */;", true],
+                ["let foo = bar;/**/ ;", true],
+                ["let foo = bar;/* */ ;", true],
+                ["let foo = bar; /**/ ;", true],
+                ["let foo = bar; /* */ ;", true],
+                ["let foo = bar;\t;", true],
+                ["let foo = bar;\t/**/;", true],
+                ["let foo = bar;\t/* */;", true],
+                ["let foo = bar;/**/\t;", true],
+                ["let foo = bar;/* */\t;", true],
+                ["let foo = bar;\t/**/\t;", true],
+                ["let foo = bar;\t/* */\t;", true],
+                ["let foo = bar;\n;", true],
+                ["let foo = bar;\n/**/;", true],
+                ["let foo = bar;\n/* */;", true],
+                ["let foo = bar;/**/\n;", true],
+                ["let foo = bar;/* */\n;", true],
+                ["let foo = bar;\n/**/\n;", true],
+                ["let foo = bar;\n/* */\n;", true]
+            ], (code, expected) => {
+                describe("when the first given is located before the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.body[0],
+                                sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1]
+                            ),
+                            expected
+                        );
+                    });
+                });
+
+                describe("when the first given is located after the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1],
+                                sourceCode.ast.body[0]
+                            ),
+                            expected
+                        );
+                    });
+                });
+            });
+        });
+
+        describe("should return true when there is at least one whitespace character between two nodes", () => {
+            leche.withData([
+                ["let foo = bar;let baz = qux;", false],
+                ["let foo = bar;/**/let baz = qux;", false],
+                ["let foo = bar;/* */let baz = qux;", false],
+                ["let foo = bar; let baz = qux;", true],
+                ["let foo = bar; /**/let baz = qux;", true],
+                ["let foo = bar; /* */let baz = qux;", true],
+                ["let foo = bar;/**/ let baz = qux;", true],
+                ["let foo = bar;/* */ let baz = qux;", true],
+                ["let foo = bar; /**/ let baz = qux;", true],
+                ["let foo = bar; /* */ let baz = qux;", true],
+                ["let foo = bar;\tlet baz = qux;", true],
+                ["let foo = bar;\t/**/let baz = qux;", true],
+                ["let foo = bar;\t/* */let baz = qux;", true],
+                ["let foo = bar;/**/\tlet baz = qux;", true],
+                ["let foo = bar;/* */\tlet baz = qux;", true],
+                ["let foo = bar;\t/**/\tlet baz = qux;", true],
+                ["let foo = bar;\t/* */\tlet baz = qux;", true],
+                ["let foo = bar;\nlet baz = qux;", true],
+                ["let foo = bar;\n/**/let baz = qux;", true],
+                ["let foo = bar;\n/* */let baz = qux;", true],
+                ["let foo = bar;/**/\nlet baz = qux;", true],
+                ["let foo = bar;/* */\nlet baz = qux;", true],
+                ["let foo = bar;\n/**/\nlet baz = qux;", true],
+                ["let foo = bar;\n/* */\nlet baz = qux;", true],
+                ["let foo = 1;let foo2 = 2; let foo3 = 3;", true]
+            ], (code, expected) => {
+                describe("when the first given is located before the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.body[0],
+                                sourceCode.ast.body[sourceCode.ast.body.length - 1]
+                            ),
+                            expected
+                        );
+                    });
+                });
+
+                describe("when the first given is located after the second", () => {
+                    it(code, () => {
+                        const ast = espree.parse(code, DEFAULT_CONFIG),
+                            sourceCode = new SourceCode(code, ast);
+
+                        assert.strictEqual(
+                            sourceCode.isSpaceBetween(
+                                sourceCode.ast.body[sourceCode.ast.body.length - 1],
+                                sourceCode.ast.body[0]
+                            ),
+                            expected
+                        );
+                    });
+                });
+            });
+        });
+
+        describe("should return false either of the arguments' location is inside the other one", () => {
+            leche.withData([
+                ["let foo = bar;", false]
+            ], (code, expected) => {
+                it(code, () => {
+                    const ast = espree.parse(code, DEFAULT_CONFIG),
+                        sourceCode = new SourceCode(code, ast);
+
+                    assert.strictEqual(
+                        sourceCode.isSpaceBetween(
+                            sourceCode.ast.tokens[0],
+                            sourceCode.ast.body[0]
+                        ),
+                        expected
+                    );
+
+                    assert.strictEqual(
+                        sourceCode.isSpaceBetween(
+                            sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1],
+                            sourceCode.ast.body[0]
+                        ),
+                        expected
+                    );
+
+                    assert.strictEqual(
+                        sourceCode.isSpaceBetween(
+                            sourceCode.ast.body[0],
+                            sourceCode.ast.tokens[0]
+                        ),
+                        expected
+                    );
+
+                    assert.strictEqual(
+                        sourceCode.isSpaceBetween(
+                            sourceCode.ast.body[0],
+                            sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1]
+                        ),
+                        expected
+                    );
+                });
+            });
+        });
+    });
+
     describe("isSpaceBetweenTokens()", () => {
         describe("should return true when there is at least one whitespace character between two tokens", () => {
             leche.withData([