Repository: eslint/eslint (js)

Issue:
Change Request: add a typedef for formatter functions
### ESLint version

v8.10.0

### What problem do you want to solve?

ESLint does not include a type definition for the signature of a formatter function.

A [(custom) formatter](https://eslint.org/docs/developer-guide/working-with-custom-formatters) is a plain function that accepts a list of lint results and an optional second parameter [`context`](https://eslint.org/docs/developer-guide/working-with-custom-formatters#the-context-argument) with properties `cwd` and `rulesMeta`. This would be the expected type of the default export of a formatter module (a [TypeScript example](https://github.com/sindresorhus/eslint-formatter-pretty/blob/decbd28/index.d.ts#L12-L15)).

By contrast, there exists a type [`Formatter`](https://eslint.org/docs/developer-guide/nodejs-api#-formatter-type) which is used for a different purpose: it is the type of a value resolved by `eslint.loadFormatter`. This is an object with a method named `format` that accepts a single argument.

### What do you think is the correct solution?

Adding a type definition for the signature of a formatter function would be useful as a guideline to work with custom formatters and other tools that deal with those formatters, in the same way the type definitions for e.g. `Rule` or `Plugin` help with rules and plugins.

### Participation

- [X] I am willing to submit a pull request for this change.

### Additional comments

I could submit a PR if desired, but I would probably need some input from the team, for example about naming conventions.

Pull request:
docs: disambiguate types `FormatterFunction` and `LoadedFormatter`
Fixes #15654

<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://eslint.org/conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/HEAD/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[x] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-change-proposal.md))
[ ] Add autofix to a rule
[ ] Add a CLI option
[x] Add something to the core
[x] Other, please explain: type definitions changed, see below.

<!--
    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)

* Add type `FormatterFunction`
* Rename type `Formatter` to `LoadedFormatter`
* Use `FormatterFunction` in the return type of `CLIEngine.getFormatter`
* Move type `LintResult` to shared types because it is referenced by `FormatterFunction`
* Fix a misspelling of `LintResult` in JSDoc
* docs: Update/clarify some text about custom formatters
* ~~docs: Rename variable `formatter` to `loadedFormatter` in code examples~~

#### Is there anything you'd like reviewers to focus on?

<!-- markdownlint-disable-file MD004 -->

I don't think there is a way to test type definitions or any other of the changes listed above at this time, so probably everything needs a careful human review.

Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index f97d93a1b..b94ed9db8 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -4,7 +4,6 @@ const path = require("path");
 
 const INTERNAL_FILES = {
     CLI_ENGINE_PATTERN: "lib/cli-engine/**/*",
-    INIT_PATTERN: "lib/init/**/*",
     LINTER_PATTERN: "lib/linter/**/*",
     RULE_TESTER_PATTERN: "lib/rule-tester/**/*",
     RULES_PATTERN: "lib/rules/**/*",
@@ -114,17 +113,7 @@ module.exports = {
             files: [INTERNAL_FILES.CLI_ENGINE_PATTERN],
             rules: {
                 "node/no-restricted-require": ["error", [
-                    ...createInternalFilesPatterns(INTERNAL_FILES.CLI_ENGINE_PATTERN),
-                    resolveAbsolutePath("lib/init/index.js")
-                ]]
-            }
-        },
-        {
-            files: [INTERNAL_FILES.INIT_PATTERN],
-            rules: {
-                "node/no-restricted-require": ["error", [
-                    ...createInternalFilesPatterns(INTERNAL_FILES.INIT_PATTERN),
-                    resolveAbsolutePath("lib/rule-tester/index.js")
+                    ...createInternalFilesPatterns(INTERNAL_FILES.CLI_ENGINE_PATTERN)
                 ]]
             }
         },
@@ -135,7 +124,6 @@ module.exports = {
                     ...createInternalFilesPatterns(INTERNAL_FILES.LINTER_PATTERN),
                     "fs",
                     resolveAbsolutePath("lib/cli-engine/index.js"),
-                    resolveAbsolutePath("lib/init/index.js"),
                     resolveAbsolutePath("lib/rule-tester/index.js")
                 ]]
             }
@@ -147,7 +135,6 @@ module.exports = {
                     ...createInternalFilesPatterns(INTERNAL_FILES.RULES_PATTERN),
                     "fs",
                     resolveAbsolutePath("lib/cli-engine/index.js"),
-                    resolveAbsolutePath("lib/init/index.js"),
                     resolveAbsolutePath("lib/linter/index.js"),
                     resolveAbsolutePath("lib/rule-tester/index.js"),
                     resolveAbsolutePath("lib/source-code/index.js")
@@ -160,7 +147,6 @@ module.exports = {
                 "node/no-restricted-require": ["error", [
                     ...createInternalFilesPatterns(),
                     resolveAbsolutePath("lib/cli-engine/index.js"),
-                    resolveAbsolutePath("lib/init/index.js"),
                     resolveAbsolutePath("lib/linter/index.js"),
                     resolveAbsolutePath("lib/rule-tester/index.js"),
                     resolveAbsolutePath("lib/source-code/index.js")
@@ -174,7 +160,6 @@ module.exports = {
                     ...createInternalFilesPatterns(INTERNAL_FILES.SOURCE_CODE_PATTERN),
                     "fs",
                     resolveAbsolutePath("lib/cli-engine/index.js"),
-                    resolveAbsolutePath("lib/init/index.js"),
                     resolveAbsolutePath("lib/linter/index.js"),
                     resolveAbsolutePath("lib/rule-tester/index.js"),
                     resolveAbsolutePath("lib/rules/index.js")
@@ -186,8 +171,7 @@ module.exports = {
             rules: {
                 "node/no-restricted-require": ["error", [
                     ...createInternalFilesPatterns(INTERNAL_FILES.RULE_TESTER_PATTERN),
-                    resolveAbsolutePath("lib/cli-engine/index.js"),
-                    resolveAbsolutePath("lib/init/index.js")
+                    resolveAbsolutePath("lib/cli-engine/index.js")
                 ]]
             }
         }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c127db46d..902358582 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+v8.12.0 - March 25, 2022
+
+* [`685a67a`](https://github.com/eslint/eslint/commit/685a67a62bdea19ca9ce12008a034b8d31162422) feat: fix logic for top-level `this` in no-invalid-this and no-eval (#15712) (Milos Djermanovic)
+* [`18f5e05`](https://github.com/eslint/eslint/commit/18f5e05bce10503186989d81ca484abb185a2c9d) chore: padding-line-between-statements remove useless `additionalItems` (#15706) (Martin Sadovy)
+
 v8.11.0 - March 11, 2022
 
 * [`6814922`](https://github.com/eslint/eslint/commit/68149221637faa8e4f2718773e751126b7ae8ac9) fix: escaping for square brackets in ignore patterns (#15666) (Milos Djermanovic)
diff --git a/README.md b/README.md
index e224151d6..f354ea861 100644
--- a/README.md
+++ b/README.md
@@ -294,7 +294,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
 <p><a href="https://contra.com"><img src="https://images.opencollective.com/contra1/c70f93f/logo.png" alt="Contra" height="96"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a></p>
+<p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a></p>
 <!--sponsorsend-->
 
 ## <a name="technology-sponsors"></a>Technology Sponsors
diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md
index 826e012ae..fc63072ad 100644
--- a/docs/developer-guide/nodejs-api.md
+++ b/docs/developer-guide/nodejs-api.md
@@ -21,7 +21,7 @@ While ESLint is designed to be run on the command line, it's possible to use ESL
     * [LintMessage type][lintmessage]
     * [SuppressedLintMessage type][suppressedlintmessage]
     * [EditInfo type][editinfo]
-    * [Formatter type][formatter]
+    * [LoadedFormatter type][loadedformatter]
 * [SourceCode](#sourcecode)
     * [splitLines()](#sourcecodesplitlines)
 * [Linter](#linter)
@@ -287,8 +287,8 @@ This method loads a formatter. Formatters convert lint results to a human- or ma
 
 #### Return Value
 
-* (`Promise<Formatter>`)<br>
-  The promise that will be fulfilled with a [Formatter] object.
+* (`Promise<LoadedFormatter>`)<br>
+  The promise that will be fulfilled with a [LoadedFormatter] object.
 
 ### ◆ ESLint.version
 
@@ -430,9 +430,9 @@ The `EditInfo` value is information to edit text. The `fix` and `suggestions` pr
 
 This edit information means replacing the range of the `range` property by the `text` property value. It's like `sourceCodeText.slice(0, edit.range[0]) + edit.text + sourceCodeText.slice(edit.range[1])`. Therefore, it's an add if the `range[0]` and `range[1]` property values are the same value, and it's removal if the `text` property value is empty string.
 
-### ◆ Formatter type
+### ◆ LoadedFormatter type
 
-The `Formatter` value is the object to convert the [LintResult] objects to text. The [eslint.loadFormatter()][eslint-loadformatter] method returns it. It has the following method:
+The `LoadedFormatter` value is the object to convert the [LintResult] objects to text. The [eslint.loadFormatter()][eslint-loadformatter] method returns it. It has the following method:
 
 * `format` (`(results: LintResult[]) => string | Promise<string>`)<br>
   The method to convert the [LintResult] objects to text.
@@ -960,5 +960,5 @@ ruleTester.run("my-rule", myRule, {
 [lintmessage]: #-lintmessage-type
 [suppressedlintmessage]: #-suppressedlintmessage-type
 [editinfo]: #-editinfo-type
-[formatter]: #-formatter-type
+[loadedformatter]: #-loadedformatter-type
 [linter]: #linter
diff --git a/docs/developer-guide/working-with-custom-formatters.md b/docs/developer-guide/working-with-custom-formatters.md
index e157bb079..bdcbdb12f 100644
--- a/docs/developer-guide/working-with-custom-formatters.md
+++ b/docs/developer-guide/working-with-custom-formatters.md
@@ -2,7 +2,7 @@
 
 While ESLint has some built-in formatters available to format the linting results, it's also possible to create and distribute your own custom formatters. You can include custom formatters in your project directly or create an npm package to distribute them separately.
 
-Each formatter is just a function that receives a `results` object and returns a string. For example, the following is how the `json` built-in formatter is implemented:
+Each formatter is just a function that receives a `results` object and a `context` and returns a string. For example, the following is how the `json` built-in formatter is implemented:
 
 ```js
 //my-awesome-formatter.js
@@ -11,7 +11,7 @@ module.exports = function(results, context) {
 };
 ```
 
-Formatter can also be an async function (from ESLint v8.4.0), the following shows a simple example:
+A formatter can also be an async function (from ESLint v8.4.0), the following shows a simple example:
 
 ```js
 //my-awesome-formatter.js
@@ -280,7 +280,7 @@ warning no-unused-vars (https://eslint.org/docs/rules/no-unused-vars)
 
 ## Passing Arguments to Formatters
 
-While custom formatter do not receive arguments in addition to the results object, it is possible to pass additional data into formatters.
+While formatter functions do not receive arguments in addition to the results object and the context, it is possible to pass additional data into custom formatters using the methods described below.
 
 ## Using Environment Variables
 
diff --git a/docs/rules/no-invalid-this.md b/docs/rules/no-invalid-this.md
index 91e460da7..bbdc79cbc 100644
--- a/docs/rules/no-invalid-this.md
+++ b/docs/rules/no-invalid-this.md
@@ -1,14 +1,18 @@
 # no-invalid-this
 
-Disallows `this` keywords outside of classes or class-like objects.
+Disallows use of `this` in contexts where the value of `this` is `undefined`.
 
 Under the strict mode, `this` keywords outside of classes or class-like objects might be `undefined` and raise a `TypeError`.
 
 ## Rule Details
 
-This rule aims to flag usage of `this` keywords outside of classes or class-like objects.
+This rule aims to flag usage of `this` keywords in contexts where the value of `this` is `undefined`.
 
-Basically, this rule checks whether or not a function containing `this` keyword is a constructor or a method.
+Top-level `this` in scripts is always considered valid because it refers to the global object regardless of the strict mode.
+
+Top-level `this` in ECMAScript modules is always considered invalid because its value is `undefined`.
+
+For `this` inside functions, this rule basically checks whether or not the function containing `this` keyword is a constructor or a method. Note that arrow functions have lexical `this`, and that therefore this rule checks their enclosing contexts.
 
 This rule judges from following conditions whether or not the function is a constructor:
 
@@ -30,6 +34,7 @@ And this rule allows `this` keywords in functions below:
 
 And this rule always allows `this` keywords in the following contexts:
 
+* At the top level of scripts.
 * In class field initializers.
 * In class static blocks.
 
@@ -46,9 +51,6 @@ Examples of **incorrect** code for this rule in strict mode:
 
 "use strict";
 
-this.a = 0;
-baz(() => this);
-
 (function() {
     this.a = 0;
     baz(() => this);
@@ -69,11 +71,6 @@ foo(function() {
     baz(() => this);
 });
 
-obj.foo = () => {
-    // `this` of arrow functions is the outer scope's.
-    this.a = 0;
-};
-
 var obj = {
     aaa: function() {
         return function foo() {
@@ -98,6 +95,9 @@ Examples of **correct** code for this rule in strict mode:
 
 "use strict";
 
+this.a = 0;
+baz(() => this);
+
 function Foo() {
     // OK, this is in a legacy style constructor.
     this.a = 0;
diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js
index 92b875578..f7aa9e224 100644
--- a/lib/cli-engine/cli-engine.js
+++ b/lib/cli-engine/cli-engine.js
@@ -56,6 +56,7 @@ const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
 /** @typedef {import("../shared/types").Plugin} Plugin */
 /** @typedef {import("../shared/types").RuleConf} RuleConf */
 /** @typedef {import("../shared/types").Rule} Rule */
+/** @typedef {import("../shared/types").FormatterFunction} FormatterFunction */
 /** @typedef {ReturnType<CascadingConfigArrayFactory.getConfigArrayForFile>} ConfigArray */
 /** @typedef {ReturnType<ConfigArray.extractConfig>} ExtractedConfig */
 
@@ -1002,7 +1003,7 @@ class CLIEngine {
      * @param {string} [format] The name of the format to load or the path to a
      *      custom formatter.
      * @throws {any} As may be thrown by requiring of formatter
-     * @returns {(Function|null)} The formatter function or null if the `format` is not a string.
+     * @returns {(FormatterFunction|null)} The formatter function or null if the `format` is not a string.
      */
     getFormatter(format) {
 
diff --git a/lib/cli-engine/formatters/html.js b/lib/cli-engine/formatters/html.js
index b63659971..6e40bbe16 100644
--- a/lib/cli-engine/formatters/html.js
+++ b/lib/cli-engine/formatters/html.js
@@ -151,7 +151,7 @@ function pageTemplate(it) {
         </script>
     </body>
 </html>
-`.trimLeft();
+`.trimStart();
 }
 
 /**
@@ -222,7 +222,7 @@ function messageTemplate(it) {
         <a href="${ruleUrl ? ruleUrl : ""}" target="_blank" rel="noopener noreferrer">${ruleId ? ruleId : ""}</a>
     </td>
 </tr>
-`.trimLeft();
+`.trimStart();
 }
 
 /**
@@ -280,7 +280,7 @@ function resultTemplate(it) {
         <span>${encodeHTML(summary)}</span>
     </th>
 </tr>
-`.trimLeft();
+`.trimStart();
 }
 
 /**
diff --git a/lib/eslint/eslint.js b/lib/eslint/eslint.js
index 1e5a8f8b1..927b60802 100644
--- a/lib/eslint/eslint.js
+++ b/lib/eslint/eslint.js
@@ -35,10 +35,11 @@ const { version } = require("../../package.json");
 /** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
 /** @typedef {import("../shared/types").Plugin} Plugin */
 /** @typedef {import("../shared/types").Rule} Rule */
+/** @typedef {import("../shared/types").LintResult} LintResult */
 
 /**
  * The main formatter object.
- * @typedef Formatter
+ * @typedef LoadedFormatter
  * @property {function(LintResult[]): string | Promise<string>} format format function.
  */
 
@@ -74,22 +75,6 @@ const { version } = require("../../package.json");
  * @property {Object} definition The plugin definition.
  */
 
-/**
- * A linting result.
- * @typedef {Object} LintResult
- * @property {string} filePath The path to the file that was linted.
- * @property {LintMessage[]} messages All of the messages for the result.
- * @property {SuppressedLintMessage[]} suppressedMessages All of the suppressed messages for the result.
- * @property {number} errorCount Number of errors for the result.
- * @property {number} fatalErrorCount Number of fatal errors for the result.
- * @property {number} warningCount Number of warnings for the result.
- * @property {number} fixableErrorCount Number of fixable errors for the result.
- * @property {number} fixableWarningCount Number of fixable warnings for the result.
- * @property {string} [source] The source code of the file that was linted.
- * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible.
- * @property {DeprecatedRuleInfo[]} usedDeprecatedRules The list of used deprecated rules.
- */
-
 /**
  * Private members for the `ESLint` instance.
  * @typedef {Object} ESLintPrivateMembers
@@ -619,7 +604,7 @@ class ESLint {
      *   - `@foo` → `@foo/eslint-formatter`
      *   - `@foo/bar` → `@foo/eslint-formatter-bar`
      * - A file path ... Load the file.
-     * @returns {Promise<Formatter>} A promise resolving to the formatter object.
+     * @returns {Promise<LoadedFormatter>} A promise resolving to the formatter object.
      * This promise will be rejected if the given formatter was not found or not
      * a function.
      */
@@ -639,7 +624,7 @@ class ESLint {
 
             /**
              * The main formatter method.
-             * @param {LintResults[]} results The lint results to format.
+             * @param {LintResult[]} results The lint results to format.
              * @returns {string | Promise<string>} The formatted lint results.
              */
             format(results) {
diff --git a/lib/linter/apply-disable-directives.js b/lib/linter/apply-disable-directives.js
index f8e4aeedb..459c85911 100644
--- a/lib/linter/apply-disable-directives.js
+++ b/lib/linter/apply-disable-directives.js
@@ -66,7 +66,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
     const listText = commentToken.value
         .slice(listStartOffset) // remove directive name and all whitespace before the list
         .split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
-        .trimRight(); // remove all whitespace after the list
+        .trimEnd(); // remove all whitespace after the list
 
     /*
      * We can assume that `listText` contains multiple elements.
diff --git a/lib/rules/no-eval.js b/lib/rules/no-eval.js
index ae6d71b80..dab04dd77 100644
--- a/lib/rules/no-eval.js
+++ b/lib/rules/no-eval.js
@@ -87,6 +87,7 @@ module.exports = {
                 upper: funcInfo,
                 node,
                 strict,
+                isTopLevelOfScript: false,
                 defaultThis: false,
                 initialized: strict
             };
@@ -222,12 +223,14 @@ module.exports = {
                     strict =
                         scope.isStrict ||
                         node.sourceType === "module" ||
-                        (features.globalReturn && scope.childScopes[0].isStrict);
+                        (features.globalReturn && scope.childScopes[0].isStrict),
+                    isTopLevelOfScript = node.sourceType !== "module" && !features.globalReturn;
 
                 funcInfo = {
                     upper: null,
                     node,
                     strict,
+                    isTopLevelOfScript,
                     defaultThis: true,
                     initialized: true
                 };
@@ -269,7 +272,8 @@ module.exports = {
                     );
                 }
 
-                if (!funcInfo.strict && funcInfo.defaultThis) {
+                // `this` at the top level of scripts always refers to the global object
+                if (funcInfo.isTopLevelOfScript || (!funcInfo.strict && funcInfo.defaultThis)) {
 
                     // `this.eval` is possible built-in `eval`.
                     report(node.parent);
diff --git a/lib/rules/no-invalid-this.js b/lib/rules/no-invalid-this.js
index 64e4d964a..a97696b85 100644
--- a/lib/rules/no-invalid-this.js
+++ b/lib/rules/no-invalid-this.js
@@ -1,5 +1,5 @@
 /**
- * @fileoverview A rule to disallow `this` keywords outside of classes or class-like objects.
+ * @fileoverview A rule to disallow `this` keywords in contexts where the value of `this` is `undefined`.
  * @author Toru Nagashima
  */
 
@@ -36,7 +36,7 @@ module.exports = {
         type: "suggestion",
 
         docs: {
-            description: "disallow `this` keywords outside of classes or class-like objects",
+            description: "disallow use of `this` in contexts where the value of `this` is `undefined`",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-invalid-this"
         },
@@ -98,11 +98,11 @@ module.exports = {
                     const scope = context.getScope();
                     const features = context.parserOptions.ecmaFeatures || {};
 
+                    // `this` at the top level of scripts always refers to the global object
                     stack.push({
                         init: true,
                         node,
                         valid: !(
-                            scope.isStrict ||
                             node.sourceType === "module" ||
                             (features.globalReturn && scope.childScopes[0].isStrict)
                         )
diff --git a/lib/shared/types.js b/lib/shared/types.js
index c407c4fb1..0335423f2 100644
--- a/lib/shared/types.js
+++ b/lib/shared/types.js
@@ -173,3 +173,27 @@ module.exports = {};
  * @property {string} ruleId The rule ID.
  * @property {string[]} replacedBy The rule IDs that replace this deprecated rule.
  */
+
+/**
+ * A linting result.
+ * @typedef {Object} LintResult
+ * @property {string} filePath The path to the file that was linted.
+ * @property {LintMessage[]} messages All of the messages for the result.
+ * @property {SuppressedLintMessage[]} suppressedMessages All of the suppressed messages for the result.
+ * @property {number} errorCount Number of errors for the result.
+ * @property {number} fatalErrorCount Number of fatal errors for the result.
+ * @property {number} warningCount Number of warnings for the result.
+ * @property {number} fixableErrorCount Number of fixable errors for the result.
+ * @property {number} fixableWarningCount Number of fixable warnings for the result.
+ * @property {string} [source] The source code of the file that was linted.
+ * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible.
+ * @property {DeprecatedRuleInfo[]} usedDeprecatedRules The list of used deprecated rules.
+ */
+
+/**
+ * A formatter function.
+ * @callback FormatterFunction
+ * @param {LintResult[]} results The list of linting results.
+ * @param {{cwd: string, rulesMeta: Record<string, RuleMeta>}} [context] A context object.
+ * @returns {string | Promise<string>} Formatted text.
+ */
diff --git a/messages/all-files-ignored.js b/messages/all-files-ignored.js
index d85828d36..70877a4d8 100644
--- a/messages/all-files-ignored.js
+++ b/messages/all-files-ignored.js
@@ -12,5 +12,5 @@ If you do want to lint these files, try the following solutions:
 
 * Check your .eslintignore file, or the eslintIgnore property in package.json, to ensure that the files are not configured to be ignored.
 * Explicitly list the files from this glob that you'd like to lint on the command-line, rather than providing a glob as an argument.
-`.trimLeft();
+`.trimStart();
 };
diff --git a/messages/extend-config-missing.js b/messages/extend-config-missing.js
index db8a5c64b..5b3498fcd 100644
--- a/messages/extend-config-missing.js
+++ b/messages/extend-config-missing.js
@@ -9,5 +9,5 @@ ESLint couldn't find the config "${configName}" to extend from. Please check tha
 The config "${configName}" was referenced from the config file in "${importerName}".
 
 If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.
-`.trimLeft();
+`.trimStart();
 };
diff --git a/messages/failed-to-read-json.js b/messages/failed-to-read-json.js
index 5114de309..e7c6cb587 100644
--- a/messages/failed-to-read-json.js
+++ b/messages/failed-to-read-json.js
@@ -7,5 +7,5 @@ module.exports = function(it) {
 Failed to read JSON file at ${path}:
 
 ${message}
-`.trimLeft();
+`.trimStart();
 };
diff --git a/messages/file-not-found.js b/messages/file-not-found.js
index 26a5d57ef..1a62fcf96 100644
--- a/messages/file-not-found.js
+++ b/messages/file-not-found.js
@@ -6,5 +6,5 @@ module.exports = function(it) {
     return `
 No files matching the pattern "${pattern}"${globDisabled ? " (with disabling globs)" : ""} were found.
 Please check for typing mistakes in the pattern.
-`.trimLeft();
+`.trimStart();
 };
diff --git a/messages/no-config-found.js b/messages/no-config-found.js
index c2f7ac73b..9860410a6 100644
--- a/messages/no-config-found.js
+++ b/messages/no-config-found.js
@@ -11,5 +11,5 @@ ESLint couldn't find a configuration file. To set up a configuration file for th
 ESLint looked for configuration files in ${directoryPath} and its ancestors. If it found none, it then looked in your home directory.
 
 If you think you already have a configuration file or if you need more help, please stop by the ESLint chat room: https://eslint.org/chat/help
-`.trimLeft();
+`.trimStart();
 };
diff --git a/messages/plugin-invalid.js b/messages/plugin-invalid.js
index 7913576f0..8b471d4a3 100644
--- a/messages/plugin-invalid.js
+++ b/messages/plugin-invalid.js
@@ -12,5 +12,5 @@ module.exports = function(it) {
 "${configName}" was referenced from the config file in "${importerName}".
 
 If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.
-`.trimLeft();
+`.trimStart();
 };
diff --git a/messages/plugin-missing.js b/messages/plugin-missing.js
index f58c78ceb..0b7d34e3a 100644
--- a/messages/plugin-missing.js
+++ b/messages/plugin-missing.js
@@ -15,5 +15,5 @@ It's likely that the plugin isn't installed correctly. Try reinstalling by runni
 The plugin "${pluginName}" was referenced from the config file in "${importerName}".
 
 If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.
-`.trimLeft();
+`.trimStart();
 };
diff --git a/messages/print-config-with-directory-path.js b/messages/print-config-with-directory-path.js
index 6a5d571dd..f65bdaaf7 100644
--- a/messages/print-config-with-directory-path.js
+++ b/messages/print-config-with-directory-path.js
@@ -4,5 +4,5 @@ module.exports = function() {
     return `
 The '--print-config' CLI option requires a path to a source code file rather than a directory.
 See also: https://eslint.org/docs/user-guide/command-line-interface#--print-config
-`.trimLeft();
+`.trimStart();
 };
diff --git a/messages/whitespace-found.js b/messages/whitespace-found.js
index 4ce49ca3a..8a801bcec 100644
--- a/messages/whitespace-found.js
+++ b/messages/whitespace-found.js
@@ -7,5 +7,5 @@ module.exports = function(it) {
 ESLint couldn't find the plugin "${pluginName}". because there is whitespace in the name. Please check your configuration and remove all whitespace from the plugin name.
 
 If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.
-`.trimLeft();
+`.trimStart();
 };
diff --git a/package.json b/package.json
index 479b085da..abb1fd109 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint",
-  "version": "8.11.0",
+  "version": "8.12.0",
   "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
   "description": "An AST-based pattern checker for JavaScript.",
   "bin": {


Test patch:
diff --git a/tests/lib/rules/no-eval.js b/tests/lib/rules/no-eval.js
index b084892cf..fdd961f65 100644
--- a/tests/lib/rules/no-eval.js
+++ b/tests/lib/rules/no-eval.js
@@ -42,6 +42,8 @@ ruleTester.run("no-eval", rule, {
         { code: "function foo() { var eval = 'foo'; globalThis[eval]('foo') }", env: { es2020: true } },
         "this.noeval('foo');",
         "function foo() { 'use strict'; this.eval('foo'); }",
+        { code: "'use strict'; this.eval('foo');", parserOptions: { ecmaFeatures: { globalReturn: true } } },
+        { code: "this.eval('foo');", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
         { code: "function foo() { this.eval('foo'); }", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
         { code: "function foo() { this.eval('foo'); }", parserOptions: { ecmaFeatures: { impliedStrict: true } } },
         "var obj = {foo: function() { this.eval('foo'); }}",
@@ -92,6 +94,7 @@ ruleTester.run("no-eval", rule, {
         { code: "(0, window['eval'])('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "MemberExpression", column: 12, endColumn: 18 }] },
         { code: "var EVAL = eval; EVAL('foo')", errors: [{ messageId: "unexpected", type: "Identifier", column: 12, endColumn: 16 }] },
         { code: "var EVAL = this.eval; EVAL('foo')", errors: [{ messageId: "unexpected", type: "MemberExpression", column: 17, endColumn: 21 }] },
+        { code: "'use strict'; var EVAL = this.eval; EVAL('foo')", errors: [{ messageId: "unexpected", type: "MemberExpression", column: 31, endColumn: 35 }] },
         { code: "(function(exe){ exe('foo') })(eval);", errors: [{ messageId: "unexpected", type: "Identifier", column: 31, endColumn: 35 }] },
         { code: "window.eval('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 8, endColumn: 12 }] },
         { code: "window.window.eval('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 15, endColumn: 19 }] },
@@ -100,6 +103,7 @@ ruleTester.run("no-eval", rule, {
         { code: "global.global.eval('foo')", env: { node: true }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 15, endColumn: 19 }] },
         { code: "global.global[`eval`]('foo')", parserOptions: { ecmaVersion: 6 }, env: { node: true }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 15, endColumn: 21 }] },
         { code: "this.eval('foo')", errors: [{ messageId: "unexpected", type: "CallExpression", column: 6, endColumn: 10 }] },
+        { code: "'use strict'; this.eval('foo')", errors: [{ messageId: "unexpected", type: "CallExpression", column: 20, endColumn: 24 }] },
         { code: "function foo() { this.eval('foo') }", errors: [{ messageId: "unexpected", type: "CallExpression", column: 23, endColumn: 27 }] },
         { code: "var EVAL = globalThis.eval; EVAL('foo')", env: { es2020: true }, errors: [{ messageId: "unexpected", type: "MemberExpression", column: 23, endColumn: 27 }] },
         { code: "globalThis.eval('foo')", env: { es2020: true }, errors: [{ messageId: "unexpected", type: "CallExpression", column: 12, endColumn: 16 }] },
@@ -134,6 +138,11 @@ ruleTester.run("no-eval", rule, {
             parserOptions: { ecmaVersion: 2022 },
             errors: [{ messageId: "unexpected" }]
         },
+        {
+            code: "'use strict'; class C { [this.eval('foo')] }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected" }]
+        },
 
         {
             code: "class A { static {} [this.eval()]; }",
diff --git a/tests/lib/rules/no-invalid-this.js b/tests/lib/rules/no-invalid-this.js
index 6b54d9a96..b106a706c 100644
--- a/tests/lib/rules/no-invalid-this.js
+++ b/tests/lib/rules/no-invalid-this.js
@@ -106,8 +106,8 @@ const patterns = [
         code: "console.log(this); z(x => console.log(x, this));",
         parserOptions: { ecmaVersion: 6 },
         errors,
-        valid: [NORMAL],
-        invalid: [USE_STRICT, IMPLIED_STRICT, MODULES]
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT],
+        invalid: [MODULES]
     },
     {
         code: "console.log(this); z(x => console.log(x, this));",
@@ -125,8 +125,17 @@ const patterns = [
             ecmaVersion: 6
         },
         errors,
-        valid: [NORMAL],
-        invalid: [USE_STRICT, IMPLIED_STRICT, MODULES]
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT],
+        invalid: [MODULES]
+    },
+    {
+        code: "this.eval('foo');",
+        parserOptions: {
+            ecmaVersion: 6
+        },
+        errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }],
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT],
+        invalid: [MODULES]
     },
 
     // IIFE.
@@ -374,8 +383,8 @@ const patterns = [
     {
         code: "obj.foo = (() => () => { console.log(this); z(x => console.log(x, this)); })();",
         parserOptions: { ecmaVersion: 6 },
-        valid: [NORMAL],
-        invalid: [USE_STRICT, IMPLIED_STRICT, MODULES],
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT],
+        invalid: [MODULES],
         errors
     },
     {
@@ -793,8 +802,8 @@ const patterns = [
     {
         code: "class C { [this.foo]; }",
         parserOptions: { ecmaVersion: 2022 },
-        valid: [NORMAL], // the global this in non-strict mode is OK.
-        invalid: [USE_STRICT, IMPLIED_STRICT, MODULES],
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT], // `this` is the top-level `this`
+        invalid: [MODULES],
         errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }]
     },
     {
@@ -855,15 +864,15 @@ const patterns = [
     {
         code: "class C { static {} [this]; }",
         parserOptions: { ecmaVersion: 2022 },
-        valid: [NORMAL],
-        invalid: [USE_STRICT, IMPLIED_STRICT, MODULES],
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT],
+        invalid: [MODULES],
         errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }]
     },
     {
         code: "class C { static {} [this.x]; }",
         parserOptions: { ecmaVersion: 2022 },
-        valid: [NORMAL],
-        invalid: [USE_STRICT, IMPLIED_STRICT, MODULES],
+        valid: [NORMAL, USE_STRICT, IMPLIED_STRICT],
+        invalid: [MODULES],
         errors: [{ messageId: "unexpectedThis", type: "ThisExpression" }]
     },