From: Thomas Lamprecht Date: Mon, 6 Dec 2021 13:02:55 +0000 (+0100) Subject: import 8.4.0 source X-Git-Url: https://git.proxmox.com/?p=pve-eslint.git;a=commitdiff_plain;h=34eeec05c48d011f9a080215e348245aed3c0db1 import 8.4.0 source Signed-off-by: Thomas Lamprecht --- diff --git a/Makefile b/Makefile index de67eb0..789a3f5 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc SRCDIR=src UPSTREAM=eslint -UPSTREAMTAG=v8.3.0 +UPSTREAMTAG=v8.4.0 BUILDSRC=${UPSTREAM}-${UPSTREAMTAG} all: ${DEB} diff --git a/eslint/.github/PULL_REQUEST_TEMPLATE.md b/eslint/.github/PULL_REQUEST_TEMPLATE.md index e15677e..e2d7566 100644 --- a/eslint/.github/PULL_REQUEST_TEMPLATE.md +++ b/eslint/.github/PULL_REQUEST_TEMPLATE.md @@ -19,7 +19,7 @@ [ ] 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 autofixing to a rule +[ ] Add autofix to a rule [ ] Add a CLI option [ ] Add something to the core [ ] Other, please explain: diff --git a/eslint/.github/workflows/ci.yml b/eslint/.github/workflows/ci.yml index d6baaa9..1087ab4 100644 --- a/eslint/.github/workflows/ci.yml +++ b/eslint/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '14.x' + node-version: '16.x' - name: Install Packages run: npm install - name: Lint Files diff --git a/eslint/.markdownlint.yml b/eslint/.markdownlint.yml index 975106d..c37cf77 100644 --- a/eslint/.markdownlint.yml +++ b/eslint/.markdownlint.yml @@ -1,11 +1,9 @@ default: true # Exclusions for deliberate/widespread violations -MD001: false # Header levels should only increment by one level at a time MD002: false # First header should be a h1 header MD007: # Unordered list indentation indent: 4 -MD012: false # Multiple consecutive blank lines MD013: false # Line length MD014: false # Dollar signs used before commands without showing output MD019: false # Multiple spaces after hash on atx style header @@ -15,7 +13,5 @@ MD026: false # Trailing punctuation in header MD029: false # Ordered list item prefix MD030: false # Spaces after list markers MD033: false # Allow inline HTML -MD034: false # Bare URL used -MD040: false # Fenced code blocks should have a language specified MD041: false # First line in file should be a top level header MD046: false # Code block style diff --git a/eslint/.markdownlintignore b/eslint/.markdownlintignore index 1b763b1..251c083 100644 --- a/eslint/.markdownlintignore +++ b/eslint/.markdownlintignore @@ -1 +1,3 @@ CHANGELOG.md +node_modules +tmp diff --git a/eslint/CHANGELOG.md b/eslint/CHANGELOG.md index abcfbb4..3b78599 100644 --- a/eslint/CHANGELOG.md +++ b/eslint/CHANGELOG.md @@ -1,3 +1,23 @@ +v8.4.0 - December 3, 2021 + +* [`efede90`](https://github.com/eslint/eslint/commit/efede90d59edc5cca9cd739df7e98f1ff00ca37d) chore: upgrade @eslint/eslintrc@1.0.5 (#15389) (Milos Djermanovic) +* [`5771663`](https://github.com/eslint/eslint/commit/5771663e8d3e86fec9454ee0af439c6989506bf3) feat: add `allowReserved` parser option (#15387) (Milos Djermanovic) +* [`32ac37a`](https://github.com/eslint/eslint/commit/32ac37a76b2e009a8f106229bc7732671d358189) feat: Flat config support in Linter (refs #13481) (#15185) (Nicholas C. Zakas) +* [`54deec5`](https://github.com/eslint/eslint/commit/54deec56bc25d516becaf767769ee7543f491d62) docs: update integrations.md (#15380) (Vlad Sholokhov) +* [`d041f34`](https://github.com/eslint/eslint/commit/d041f345cdf0306f15faa2f305fe1d21ef137eb1) feat: Treat Class/New Expressions as truthy in no-constant-condition (#15326) (Jordan Eldredge) +* [`8f44cf5`](https://github.com/eslint/eslint/commit/8f44cf505765b663e371412ab57f0f1dbbe78513) feat: report only lines that exceed the limit in max-lines-per-function (#15140) (Sneh Khatri) +* [`fa0423a`](https://github.com/eslint/eslint/commit/fa0423af7f8453f6c97b915b3b026f258b76a600) docs: fix typo in PR template (#15365) (Nitin Kumar) +* [`0b8c846`](https://github.com/eslint/eslint/commit/0b8c846c77234125fbb211980bc1e62dc8791513) chore: fix update-readme to avoid multiple consecutive blank lines (#15375) (Milos Djermanovic) +* [`808ad35`](https://github.com/eslint/eslint/commit/808ad35f204c838cd5eb8d766807dc43692f42f9) feat: pass cwd to formatters (refs eslint/rfcs#57) (#13392) (Toru Nagashima) +* [`94b2a8b`](https://github.com/eslint/eslint/commit/94b2a8b3d1f7d139dd6b06216a64727b7d5f009b) chore: Use default Chromium binary in M1 Mac tests (#15371) (Brandon Mills) +* [`4940cc5`](https://github.com/eslint/eslint/commit/4940cc5c4903a691fe51d409137dd573c4c7706e) fix: mark --rulesdir option as deprecated in CLI docs (#15310) (Kevin Partington) +* [`e233920`](https://github.com/eslint/eslint/commit/e233920857e282ba22116ad5f1dcc6dfabc8ef5b) docs: enable a few more markdownlint rules and fix violations (#15368) (Bryan Mishkin) +* [`ba58d94`](https://github.com/eslint/eslint/commit/ba58d94cb51d4d2644c024446d5750eaf4853129) ci: use node `v16` for Verify Files (#15364) (Nitin Kumar) +* [`632176d`](https://github.com/eslint/eslint/commit/632176dc43180ea4e7f99da429fee3ee3814a04d) docs: Dedent needlessly indented example in getter-return docs (#15363) (Jordan Eldredge) +* [`4497e88`](https://github.com/eslint/eslint/commit/4497e880248c24dc19eea8a5466555b847c0c7eb) docs: Update release notes blog post template (#15285) (Nicholas C. Zakas) +* [`f1b7499`](https://github.com/eslint/eslint/commit/f1b7499a5162d3be918328ce496eb80692353a5a) feat: support async formatters (#15243) (MO) +* [`1e32ee5`](https://github.com/eslint/eslint/commit/1e32ee591e978188b121604d0af9cbc04a50a3b5) chore: add jsdoc type annotation to rules (#15291) (Bryan Mishkin) + v8.3.0 - November 21, 2021 * [`60b0a29`](https://github.com/eslint/eslint/commit/60b0a292efd1b9cdc318b1e88a0cb7bbf14860b1) feat: add `allowProperties` option to require-atomic-updates (#15238) (Milos Djermanovic) diff --git a/eslint/CONTRIBUTING.md b/eslint/CONTRIBUTING.md index 9260dfe..8f2246f 100644 --- a/eslint/CONTRIBUTING.md +++ b/eslint/CONTRIBUTING.md @@ -24,4 +24,4 @@ Please sign our [Contributor License Agreement](https://cla.js.foundation/eslint ## Full Documentation Our full contribution guidelines can be found at: -https://eslint.org/docs/developer-guide/contributing/ + diff --git a/eslint/Makefile.js b/eslint/Makefile.js index 0a0846b..3730e9b 100644 --- a/eslint/Makefile.js +++ b/eslint/Makefile.js @@ -789,6 +789,8 @@ target.checkRuleFiles = function() { RULE_FILES.forEach(filename => { const basename = path.basename(filename, ".js"); const docFilename = `docs/rules/${basename}.md`; + const docText = cat(docFilename); + const ruleCode = cat(filename); /** * Check if basename is present in rule-types.json file. @@ -806,7 +808,6 @@ target.checkRuleFiles = function() { * @private */ function hasIdInTitle(id) { - const docText = cat(docFilename); const idOldAtEndOfTitleRegExp = new RegExp(`^# (.*?) \\(${id}\\)`, "u"); // original format const idNewAtBeginningOfTitleRegExp = new RegExp(`^# ${id}: `, "u"); // new format is same as rules index /* @@ -824,14 +825,23 @@ target.checkRuleFiles = function() { * @private */ function hasDeprecatedInfo() { - const ruleCode = cat(filename); const deprecatedTagRegExp = /@deprecated in ESLint/u; - const docText = cat(docFilename); const deprecatedInfoRegExp = /This rule was .+deprecated.+in ESLint/u; return deprecatedTagRegExp.test(ruleCode) && deprecatedInfoRegExp.test(docText); } + /** + * Check if the rule code has the jsdoc comment with the rule type annotation. + * @returns {boolean} true if present + * @private + */ + function hasRuleTypeJSDocComment() { + const comment = "/** @type {import('../shared/types').Rule} */"; + + return ruleCode.includes(comment); + } + // check for docs if (!test("-f", docFilename)) { console.error("Missing documentation for rule %s", basename); @@ -880,6 +890,11 @@ target.checkRuleFiles = function() { errors++; } } + + if (!hasRuleTypeJSDocComment()) { + console.error(`Missing rule type JSDoc comment from ${basename} rule code.`); + errors++; + } } // check for tests diff --git a/eslint/README.md b/eslint/README.md index bbc74b7..e8b005d 100644 --- a/eslint/README.md +++ b/eslint/README.md @@ -47,19 +47,19 @@ Prerequisites: [Node.js](https://nodejs.org/) (`^12.22.0`, `^14.17.0`, or `>=16. You can install ESLint using npm: -``` +```sh $ npm install eslint --save-dev ``` You should then set up a configuration file: -``` +```sh $ ./node_modules/.bin/eslint --init ``` After that, you can run ESLint on any file or directory like this: -``` +```sh $ ./node_modules/.bin/eslint yourfile.js ``` @@ -207,8 +207,6 @@ These folks keep the project moving and are resources for help. - - ### Technical Steering Committee (TSC) @@ -232,7 +230,6 @@ Milos Djermanovic - ### Reviewers The people who review and implement new features. @@ -249,9 +246,6 @@ Toru Nagashima - - - ### Committers The people who review and fix bugs and help triage issues. @@ -288,12 +282,8 @@ Nitin Kumar - - - - ## Sponsors The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://opencollective.com/eslint) to get your logo on our README and website. @@ -301,10 +291,10 @@ The following companies, organizations, and individuals support ESLint's ongoing

Platinum Sponsors

-

Automattic

Gold Sponsors

-

Nx (by Nrwl) Chrome's Web Framework & Tools Performance Fund Salesforce Airbnb Coinbase American Express Substack

Silver Sponsors

+

Automattic

Gold Sponsors

+

Contra Nx (by Nrwl) Chrome's Web Framework & Tools Performance Fund Salesforce Airbnb Coinbase American Express Substack

Silver Sponsors

Liftoff

Bronze Sponsors

-

launchdarkly TROYPOINT Anagram Solver VPS Server Icons8: free icons, photos, illustrations, and music Discord ThemeIsle Fire Stick Tricks Practice Ignition

+

SumatoSoft Anagram Solver VPS Server Icons8: free icons, photos, illustrations, and music Discord ThemeIsle Fire Stick Tricks Practice Ignition

## Technology Sponsors diff --git a/eslint/conf/globals.js b/eslint/conf/globals.js new file mode 100644 index 0000000..076ffb2 --- /dev/null +++ b/eslint/conf/globals.js @@ -0,0 +1,144 @@ +/** + * @fileoverview Globals for ecmaVersion/sourceType + * @author Nicholas C. Zakas + */ + +"use strict"; + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- + +const commonjs = { + exports: true, + global: false, + module: false, + require: false +}; + +const es3 = { + Array: false, + Boolean: false, + constructor: false, + Date: false, + decodeURI: false, + decodeURIComponent: false, + encodeURI: false, + encodeURIComponent: false, + Error: false, + escape: false, + eval: false, + EvalError: false, + Function: false, + hasOwnProperty: false, + Infinity: false, + isFinite: false, + isNaN: false, + isPrototypeOf: false, + Math: false, + NaN: false, + Number: false, + Object: false, + parseFloat: false, + parseInt: false, + propertyIsEnumerable: false, + RangeError: false, + ReferenceError: false, + RegExp: false, + String: false, + SyntaxError: false, + toLocaleString: false, + toString: false, + TypeError: false, + undefined: false, + unescape: false, + URIError: false, + valueOf: false +}; + +const es5 = { + ...es3, + JSON: false +}; + +const es2015 = { + ...es5, + ArrayBuffer: false, + DataView: false, + Float32Array: false, + Float64Array: false, + Int16Array: false, + Int32Array: false, + Int8Array: false, + Map: false, + Promise: false, + Proxy: false, + Reflect: false, + Set: false, + Symbol: false, + Uint16Array: false, + Uint32Array: false, + Uint8Array: false, + Uint8ClampedArray: false, + WeakMap: false, + WeakSet: false +}; + +// no new globals in ES2016 +const es2016 = { + ...es2015 +}; + +const es2017 = { + ...es2016, + Atomics: false, + SharedArrayBuffer: false +}; + +// no new globals in ES2018 +const es2018 = { + ...es2017 +}; + +// no new globals in ES2019 +const es2019 = { + ...es2018 +}; + +const es2020 = { + ...es2019, + BigInt: false, + BigInt64Array: false, + BigUint64Array: false, + globalThis: false +}; + +const es2021 = { + ...es2020, + AggregateError: false, + FinalizationRegistry: false, + WeakRef: false +}; + +const es2022 = { + ...es2021 +}; + + +//----------------------------------------------------------------------------- +// Exports +//----------------------------------------------------------------------------- + +module.exports = { + commonjs, + es3, + es5, + es2015, + es2016, + es2017, + es2018, + es2019, + es2020, + es2021, + es2022 +}; diff --git a/eslint/docs/developer-guide/code-path-analysis.md b/eslint/docs/developer-guide/code-path-analysis.md index f22eb8f..2b01bc7 100644 --- a/eslint/docs/developer-guide/code-path-analysis.md +++ b/eslint/docs/developer-guide/code-path-analysis.md @@ -189,8 +189,6 @@ bar(); ![Loop Event's Example 5](./code-path-analysis/loop-event-example-for-5.svg) - - ## Usage Examples ### To check whether or not this is reachable diff --git a/eslint/docs/developer-guide/contributing/pull-requests.md b/eslint/docs/developer-guide/contributing/pull-requests.md index dde1c31..58fe6f9 100644 --- a/eslint/docs/developer-guide/contributing/pull-requests.md +++ b/eslint/docs/developer-guide/contributing/pull-requests.md @@ -30,7 +30,7 @@ Details about each step are found below. The first step to sending a pull request is to create a new branch in your ESLint fork. Give the branch a descriptive name that describes what it is you're fixing, such as: -``` +```sh $ git checkout -b issue1234 ``` @@ -42,14 +42,14 @@ You should do all of your development for the issue in this branch. Make the changes to the code and tests, following the [code conventions](../code-conventions.md) as you go. Once you have finished, commit the changes to your branch: -``` +```sh $ git add -A $ git commit ``` All ESLint projects follow [Conventional Commits](https://www.conventionalcommits.org/) for our commit messages. Here's an example commit message: -``` +```pt tag: Short description of what you did Longer description here if necessary @@ -79,7 +79,7 @@ The message summary should be a one-sentence description of the change, and it m Here are some good commit message summary examples: -``` +```pt build: Update Travis to only test Node 0.10 fix: Semi rule incorrectly flagging extra semicolon chore: Upgrade Esprima to 1.2, switch to using comment attachment @@ -91,7 +91,7 @@ The commit message format is important because these messages are used to create Before you send the pull request, be sure to rebase onto the upstream source. This ensures your code is running on the latest available code. -``` +```sh git fetch upstream git rebase upstream/main ``` @@ -100,7 +100,7 @@ git rebase upstream/main After rebasing, be sure to run all of the tests once again to make sure nothing broke: -``` +```sh npm test ``` @@ -123,13 +123,13 @@ With your code ready to go, this is a good time to double-check your submission Next, push your changes to your clone: -``` +```sh git push origin issue1234 ``` If you are unable to push because some references are old, do a forced push instead: -``` +```sh git push -f origin issue1234 ``` @@ -149,13 +149,13 @@ Once your pull request is sent, it's time for the team to review it. As such, pl If your commit message is in the incorrect format, you'll be asked to update it. You can do so via: -``` +```sh $ git commit --amend ``` This will open up your editor so you can make changes. After that, you'll need to do a forced push to your branch: -``` +```sh $ git push origin issue1234 -f ``` @@ -163,7 +163,7 @@ $ git push origin issue1234 -f If we ask you to make code changes, there's no need to close the pull request and create a new one. Just go back to the branch on your fork and make your changes. Then, when you're ready, you can add your changes into the branch: -``` +```sh $ git add -A $ git commit $ git push origin issue1234 @@ -177,13 +177,13 @@ The commit messages in subsequent commits do not need to be in any specific form If your code is out-of-date, we might ask you to rebase. That means we want you to apply your changes on top of the latest upstream code. Make sure you have set up a [development environment](../development-environment.md) and then you can rebase using these commands: -``` +```sh $ git fetch upstream $ git rebase upstream/main ``` You might find that there are merge conflicts when you attempt to rebase. Please [resolve the conflicts](https://help.github.com/articles/resolving-merge-conflicts-after-a-git-rebase/) and then do a forced push to your branch: -``` +```sh $ git push origin issue1234 -f ``` diff --git a/eslint/docs/developer-guide/development-environment.md b/eslint/docs/developer-guide/development-environment.md index 7981c66..4828476 100644 --- a/eslint/docs/developer-guide/development-environment.md +++ b/eslint/docs/developer-guide/development-environment.md @@ -14,7 +14,7 @@ Go to and click the "Fork" button. Follow the Once you've cloned the repository, run `npm install` to get all the necessary dependencies: -``` +```sh $ cd eslint $ npm install ``` @@ -27,7 +27,7 @@ The *upstream source* is the main ESLint repository where active development hap To add the upstream source for ESLint, run the following in your repository: -``` +```sh git remote add upstream git@github.com:eslint/eslint.git ``` @@ -49,7 +49,7 @@ Please see the [generator documentation](https://github.com/eslint/generator-esl Running the tests is the best way to ensure you have correctly set up your development environment. Make sure you're in the `eslint` directory and run: -``` +```sh npm test ``` diff --git a/eslint/docs/developer-guide/nodejs-api.md b/eslint/docs/developer-guide/nodejs-api.md index 378b9c1..ca07415 100644 --- a/eslint/docs/developer-guide/nodejs-api.md +++ b/eslint/docs/developer-guide/nodejs-api.md @@ -404,7 +404,7 @@ This edit information means replacing the range of the `range` property by the ` 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: -* `format` (`(results: LintResult[]) => string`)
+* `format` (`(results: LintResult[]) => string | Promise`)
The method to convert the [LintResult] objects to text. --- @@ -473,7 +473,7 @@ const linter2 = new Linter(); ``` In this example, rules run on `linter1` will get `path/to/project` when calling `context.getCwd()`. -Those run on `linter2` will get `process.cwd()` if the global `process` object is defined or `undefined` otherwise (e.g. on the browser https://eslint.org/demo). +Those run on `linter2` will get `process.cwd()` if the global `process` object is defined or `undefined` otherwise (e.g. on the browser ). ### Linter#verify @@ -783,7 +783,7 @@ Any additional properties of a test case will be passed directly to the linter a If a valid test case only uses the `code` property, it can optionally be provided as a string containing the code, rather than an object with a `code` key. -#### Testing errors with `messageId` +### Testing errors with `messageId` If the rule under test uses `messageId`s, you can use `messageId` property in a test case to assert reported error's `messageId` instead of its `message`. @@ -805,7 +805,7 @@ For messages with placeholders, a test case can also use `data` property to addi Please note that `data` in a test case does not assert `data` passed to `context.report`. Instead, it is used to form the expected message text which is then compared with the received `message`. -#### Testing Suggestions +### Testing Suggestions Suggestions can be tested by defining a `suggestions` key on an errors object. The options to check for the suggestions are the following (all are optional): diff --git a/eslint/docs/developer-guide/shareable-configs.md b/eslint/docs/developer-guide/shareable-configs.md index 7b57f3f..15032d9 100644 --- a/eslint/docs/developer-guide/shareable-configs.md +++ b/eslint/docs/developer-guide/shareable-configs.md @@ -32,9 +32,11 @@ Once your shareable config is ready, you can [publish to npm](https://docs.npmjs You should declare your dependency on ESLint in `package.json` using the [peerDependencies](https://docs.npmjs.com/files/package.json#peerdependencies) field. The recommended way to declare a dependency for future proof compatibility is with the ">=" range syntax, using the lowest required ESLint version. For example: -``` -"peerDependencies": { - "eslint": ">= 3" +```json +{ + "peerDependencies": { + "eslint": ">= 3" + } } ``` @@ -76,7 +78,6 @@ You can also omit the `eslint-config-` and it will be automatically assumed by E npm [scoped modules](https://docs.npmjs.com/misc/scope) are also supported in a number of ways. - By using the module name: ```json diff --git a/eslint/docs/developer-guide/working-with-custom-formatters.md b/eslint/docs/developer-guide/working-with-custom-formatters.md index a5e9ade..54db663 100644 --- a/eslint/docs/developer-guide/working-with-custom-formatters.md +++ b/eslint/docs/developer-guide/working-with-custom-formatters.md @@ -6,11 +6,21 @@ Each formatter is just a function that receives a `results` object and returns a ```js //my-awesome-formatter.js -module.exports = function(results) { +module.exports = function(results, context) { return JSON.stringify(results, null, 2); }; ``` +Formatter can also be an async function (from ESLint v8.4.0), the following shows a simple example: + +```js +//my-awesome-formatter.js +module.exports = async function(results) { + const formatted = await asyncTask(); + return formatted; +}; +``` + To run ESLint with this formatter, you can use the `-f` (or `--format`) command line flag: ```bash @@ -19,35 +29,6 @@ eslint -f ./my-awesome-formatter.js src/ In order to use a local file as a custom formatter, you must begin the filename with a dot (such as `./my-awesome-formatter.js` or `../formatters/my-awesome-formatter.js`). -### The `data` Argument - -The exported function receives an optional second argument named `data`. The `data` object provides extended information related to the analysis results. Currently, the `data` object consists of a single property named `rulesMeta`. This property is a dictionary of rule metadata, keyed with `ruleId`. The value for each entry is the `meta` property from the corresponding rule object. The dictionary contains an entry for each rule that was run during the analysis. - -Here's what the `data` object would look like if one rule, `no-extra-semi`, had been run: - -```js -{ - rulesMeta: { - "no-extra-semi": { - type: "suggestion", - docs: { - description: "disallow unnecessary semicolons", - category: "Possible Errors", - recommended: true, - url: "https://eslint.org/docs/rules/no-extra-semi" - }, - fixable: "code", - schema: [], - messages: { - unexpected: "Unnecessary semicolon." - } - } - } -} -``` - -The [Using Rule metadata](#using-rule-metadata) example shows how to use the `data` object in a custom formatter. See the [Working with Rules](https://eslint.org/docs/developer-guide/working-with-rules) page for more information about rules. - ## Packaging the Custom Formatter Custom formatters can also be distributed through npm packages. To do so, create an npm package with a name in the format of `eslint-formatter-*`, where `*` is the name of your formatter (such as `eslint-formatter-awesome`). Projects should then install the package and can use the custom formatter with the `-f` (or `--format`) flag like this: @@ -68,14 +49,14 @@ Tips for `package.json`: See all [formatters on npm](https://www.npmjs.com/search?q=eslint-formatter); -## The `results` Object +## The `results` Argument The `results` object passed into a formatter is an array of objects containing the lint results for individual files. Here's some example output: ```js [ { - filePath: "path/to/file.js", + filePath: "/path/to/a/file.js", messages: [ { ruleId: "curly", @@ -102,7 +83,7 @@ The `results` object passed into a formatter is an array of objects containing t "var err = doStuff();\nif (err) console.log('failed tests: ' + err);\nprocess.exit(1);\n" }, { - filePath: "Gruntfile.js", + filePath: "/path/to/Gruntfile.js", messages: [], errorCount: 0, warningCount: 0, @@ -137,6 +118,38 @@ Each `message` object contains information about the ESLint rule that was trigge * **column**: the column where the issue is located. * **nodeType**: the type of the node in the [AST](https://github.com/estree/estree/blob/master/spec.md#node-objects) +## The `context` Argument + +The formatter function receives an object as the second argument. The object has two properties: + +* `cwd` ... The current working directory. This value comes from the `cwd` constructor option of the [ESLint](nodejs-api.md#-new-eslintoptions) class. +* `rulesMeta` ... The `meta` property values of rules. See the [Working with Rules](working-with-rules.md) page for more information about rules. + +For example, here's what the object would look like if one rule, `no-extra-semi`, had been run: + +```js +{ + cwd: "/path/to/cwd", + rulesMeta: { + "no-extra-semi": { + type: "suggestion", + docs: { + description: "disallow unnecessary semicolons", + recommended: true, + url: "https://eslint.org/docs/rules/no-extra-semi" + }, + fixable: "code", + schema: [], + messages: { + unexpected: "Unnecessary semicolon." + } + } + } +} +``` + +**Note:** if a linting is executed by deprecated `CLIEngine` class, the `context` argument may be a different value because it is up to the API users. Please check whether the `context` argument is an expected value or not if you want to support legacy environments. + ## Examples ### Summary formatter @@ -144,7 +157,7 @@ Each `message` object contains information about the ESLint rule that was trigge A formatter that only cares about the total count of errors and warnings will look like this: ```javascript -module.exports = function(results) { +module.exports = function(results, context) { // accumulate the errors and warnings var summary = results.reduce( function(seq, current) { @@ -186,7 +199,7 @@ Errors: 2, Warnings: 4 A more complex report will look something like this: ```javascript -module.exports = function(results, data) { +module.exports = function(results, context) { var results = results || []; var summary = results.reduce( @@ -195,7 +208,7 @@ module.exports = function(results, data) { var logMessage = { filePath: current.filePath, ruleId: msg.ruleId, - ruleUrl: data.rulesMeta[msg.ruleId].docs.url, + ruleUrl: context.rulesMeta[msg.ruleId].docs.url, message: msg.message, line: msg.line, column: msg.column @@ -348,7 +361,6 @@ error semi src/configs/bundler.js:6:10 ``` - ### Complex Argument Passing If you find the custom formatter pattern doesn't provide enough options for the way you'd like to format ESLint results, the best option is to use ESLint's built-in [JSON formatter](https://eslint.org/docs/user-guide/formatters/) and pipe the output to a second program. For example: diff --git a/eslint/docs/developer-guide/working-with-rules.md b/eslint/docs/developer-guide/working-with-rules.md index ee5a560..697b7ed 100644 --- a/eslint/docs/developer-guide/working-with-rules.md +++ b/eslint/docs/developer-guide/working-with-rules.md @@ -24,9 +24,7 @@ Here is the basic format of the source file for a rule: // Rule Definition //------------------------------------------------------------------------------ -/** - * @type {import('eslint').Rule.RuleModule} - */ +/** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/docs/rules/accessor-pairs.md b/eslint/docs/rules/accessor-pairs.md index 25028d9..22cf28a 100644 --- a/eslint/docs/rules/accessor-pairs.md +++ b/eslint/docs/rules/accessor-pairs.md @@ -232,7 +232,6 @@ const Quux = class { } ``` - ## Known Limitations Due to the limits of static analysis, this rule does not account for possible side effects and in certain cases diff --git a/eslint/docs/rules/array-bracket-newline.md b/eslint/docs/rules/array-bracket-newline.md index eed3229..bb990c7 100644 --- a/eslint/docs/rules/array-bracket-newline.md +++ b/eslint/docs/rules/array-bracket-newline.md @@ -267,7 +267,6 @@ var e = [ ]; ``` - ## When Not To Use It If you don't want to enforce line breaks after opening and before closing array brackets, don't enable this rule. diff --git a/eslint/docs/rules/array-callback-return.md b/eslint/docs/rules/array-callback-return.md index 84bf0e7..568e014 100644 --- a/eslint/docs/rules/array-callback-return.md +++ b/eslint/docs/rules/array-callback-return.md @@ -144,7 +144,6 @@ myArray.forEach(item => { }); ``` - ## Known Limitations This rule checks callback functions of methods with the given names, *even if* the object which has the method is *not* an array. diff --git a/eslint/docs/rules/arrow-parens.md b/eslint/docs/rules/arrow-parens.md index 144ebdf..8a133cd 100644 --- a/eslint/docs/rules/arrow-parens.md +++ b/eslint/docs/rules/arrow-parens.md @@ -20,7 +20,6 @@ a => {} Following this style will help you find arrow functions (`=>`) which may be mistakenly included in a condition when a comparison such as `>=` was the intent. - ```js /*eslint-env es6*/ diff --git a/eslint/docs/rules/callback-return.md b/eslint/docs/rules/callback-return.md index f425ccd..c4e7277 100644 --- a/eslint/docs/rules/callback-return.md +++ b/eslint/docs/rules/callback-return.md @@ -165,7 +165,6 @@ There are some cases where you might want to call a callback function more than may lead to incorrect behavior. In those cases you may want to reserve a special name for those callbacks and not include that in the list of callbacks that trigger warnings. - ## Further Reading * [The Art Of Node: Callbacks](https://github.com/maxogden/art-of-node#callbacks) diff --git a/eslint/docs/rules/class-methods-use-this.md b/eslint/docs/rules/class-methods-use-this.md index 73efc9d..7e25b06 100644 --- a/eslint/docs/rules/class-methods-use-this.md +++ b/eslint/docs/rules/class-methods-use-this.md @@ -99,7 +99,7 @@ This rule has two options: ### exceptMethods -``` +```js "class-methods-use-this": [, { "exceptMethods": [<...exceptions>] }] ``` @@ -131,7 +131,7 @@ class A { ## enforceForClassFields -``` +```js "class-methods-use-this": [, { "enforceForClassFields": true | false }] ``` diff --git a/eslint/docs/rules/comma-spacing.md b/eslint/docs/rules/comma-spacing.md index 2310771..3160f86 100644 --- a/eslint/docs/rules/comma-spacing.md +++ b/eslint/docs/rules/comma-spacing.md @@ -110,13 +110,11 @@ var arr = [,2 ,3] If your project will not be following a consistent comma-spacing pattern, turn this rule off. - ## Further Reading * [JavaScript](http://javascript.crockford.com/code.html) * [Dojo Style Guide](https://dojotoolkit.org/reference-guide/1.9/developer/styleguide.html) - ## Related Rules * [array-bracket-spacing](array-bracket-spacing.md) diff --git a/eslint/docs/rules/comma-style.md b/eslint/docs/rules/comma-style.md index 24b5515..f71e310 100644 --- a/eslint/docs/rules/comma-style.md +++ b/eslint/docs/rules/comma-style.md @@ -157,7 +157,6 @@ var o = {fst:1, This rule can safely be turned off if your project does not care about enforcing a consistent comma style. - ## Further Reading For more information on the Comma First style: @@ -165,7 +164,6 @@ For more information on the Comma First style: * [A better coding convention for lists and object literals in JavaScript by isaacs](https://gist.github.com/isaacs/357981) * [npm coding style guideline](https://docs.npmjs.com/misc/coding-style) - ## Related Rules * [operator-linebreak](operator-linebreak.md) diff --git a/eslint/docs/rules/function-call-argument-newline.md b/eslint/docs/rules/function-call-argument-newline.md index 01c1ff4..abe0d49 100644 --- a/eslint/docs/rules/function-call-argument-newline.md +++ b/eslint/docs/rules/function-call-argument-newline.md @@ -14,7 +14,6 @@ This rule has a string option: * `"never"` disallows line breaks between arguments * `"consistent"` requires consistent usage of line breaks between arguments - ### always Examples of **incorrect** code for this rule with the default `"always"` option: @@ -69,7 +68,6 @@ baz( ); ``` - ### never Examples of **incorrect** code for this rule with the `"never"` option: @@ -189,7 +187,6 @@ baz( ); ``` - ## When Not To Use It If you don't want to enforce line breaks between arguments, don't enable this rule. diff --git a/eslint/docs/rules/getter-return.md b/eslint/docs/rules/getter-return.md index c49f39d..631de80 100644 --- a/eslint/docs/rules/getter-return.md +++ b/eslint/docs/rules/getter-return.md @@ -3,17 +3,17 @@ The get syntax binds an object property to a function that will be called when that property is looked up. It was first introduced in ECMAScript 5: ```js - var p = { - get name(){ - return "nicholas"; - } - }; - - Object.defineProperty(p, "age", { - get: function (){ - return 17; - } - }); +var p = { + get name(){ + return "nicholas"; + } +}; + +Object.defineProperty(p, "age", { + get: function (){ + return 17; + } +}); ``` Note that every `getter` is expected to return a value. diff --git a/eslint/docs/rules/indent.md b/eslint/docs/rules/indent.md index 74fb0e7..bd4e86e 100644 --- a/eslint/docs/rules/indent.md +++ b/eslint/docs/rules/indent.md @@ -842,7 +842,6 @@ if (foo) { } ``` - ## Compatibility * **JSHint**: `indent` diff --git a/eslint/docs/rules/line-comment-position.md b/eslint/docs/rules/line-comment-position.md index 7344534..2e0eef8 100644 --- a/eslint/docs/rules/line-comment-position.md +++ b/eslint/docs/rules/line-comment-position.md @@ -11,7 +11,6 @@ var foo = "bar"; // beside comment This rule enforces consistent position of line comments. Block comments are not affected by this rule. By default, this rule ignores comments starting with the following words: `eslint`, `jshint`, `jslint`, `istanbul`, `global`, `exported`, `jscs`, `falls through`. - ## Options This rule takes one argument, which can be a string or an object. The string settings are the same as those of the `position` property (explained below). The object option has the following properties: @@ -33,7 +32,6 @@ Examples of **correct** code for the `{ "position": "above" }` option: 1 + 1; ``` - Examples of **incorrect** code for the `{ "position": "above" }` option: ```js @@ -50,7 +48,6 @@ Examples of **correct** code for the `{ "position": "beside" }` option: 1 + 1; // valid comment ``` - Examples of **incorrect** code for the `{ "position": "beside" }` option: ```js diff --git a/eslint/docs/rules/linebreak-style.md b/eslint/docs/rules/linebreak-style.md index d2aa5a1..a4eb620 100644 --- a/eslint/docs/rules/linebreak-style.md +++ b/eslint/docs/rules/linebreak-style.md @@ -19,7 +19,6 @@ This rule has a string option: * `"unix"` (default) enforces the usage of Unix line endings: `\n` for LF. * `"windows"` enforces the usage of Windows line endings: `\r\n` for CRLF. - ### unix Examples of **incorrect** code for this rule with the default `"unix"` option: @@ -73,7 +72,7 @@ Version control systems sometimes have special behavior for linebreaks. To make For example, the default behavior of [git](https://git-scm.com/) on Windows systems is to convert LF linebreaks to CRLF when checking out files, but to store the linebreaks as LF when committing a change. This will cause the `linebreak-style` rule to report errors if configured with the `"unix"` setting, because the files that ESLint sees will have CRLF linebreaks. If you use git, you may want to add a line to your [`.gitattributes` file](https://git-scm.com/docs/gitattributes) to prevent git from converting linebreaks in `.js` files: -``` +```pt *.js text eol=lf ``` diff --git a/eslint/docs/rules/lines-around-comment.md b/eslint/docs/rules/lines-around-comment.md index fb01611..b13d525 100644 --- a/eslint/docs/rules/lines-around-comment.md +++ b/eslint/docs/rules/lines-around-comment.md @@ -26,7 +26,6 @@ This rule has an object option: * `"applyDefaultIgnorePatterns"` enables or disables the default comment patterns to be ignored by the rule * `"ignorePattern"` custom patterns to be ignored by the rule - ### beforeBlockComment Examples of **incorrect** code for this rule with the default `{ "beforeBlockComment": true }` option: @@ -515,7 +514,6 @@ const [ ] = ["great", "not great"]; ``` - ### ignorePattern By default this rule ignores comments starting with the following words: `eslint`, `jshint`, `jslint`, `istanbul`, `global`, `exported`, `jscs`. To ignore more comments in addition to the defaults, set the `ignorePattern` option to a string pattern that will be passed to the [`RegExp` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp). @@ -568,7 +566,6 @@ foo(); ``` - ## When Not To Use It Many people enjoy a terser code style and don't mind comments bumping up against code. If you fall into that category this rule is not for you. diff --git a/eslint/docs/rules/max-lines-per-function.md b/eslint/docs/rules/max-lines-per-function.md index d3b1361..f2af93b 100644 --- a/eslint/docs/rules/max-lines-per-function.md +++ b/eslint/docs/rules/max-lines-per-function.md @@ -10,7 +10,7 @@ This rule enforces a maximum number of lines per function, in order to aid in ma Nested long method chains like the below example are often broken onto separate lines for readability: -``` +```js function() { return m("div", [ m("table", {className: "table table-striped latest-data"}, [ diff --git a/eslint/docs/rules/no-compare-neg-zero.md b/eslint/docs/rules/no-compare-neg-zero.md index e9de786..cfbe545 100644 --- a/eslint/docs/rules/no-compare-neg-zero.md +++ b/eslint/docs/rules/no-compare-neg-zero.md @@ -31,5 +31,3 @@ if (Object.is(x, -0)) { // doSomething()... } ``` - - diff --git a/eslint/docs/rules/no-constant-condition.md b/eslint/docs/rules/no-constant-condition.md index 036bbb7..8de5e69 100644 --- a/eslint/docs/rules/no-constant-condition.md +++ b/eslint/docs/rules/no-constant-condition.md @@ -32,6 +32,14 @@ if (x &&= false) { doSomethingNever(); } +if (class {}) { + doSomethingAlways(); +} + +if (new Boolean(x)) { + doSomethingAlways(); +} + if (x ||= true) { doSomethingAlways(); } diff --git a/eslint/docs/rules/no-empty-function.md b/eslint/docs/rules/no-empty-function.md index 0f8691c..1911031 100644 --- a/eslint/docs/rules/no-empty-function.md +++ b/eslint/docs/rules/no-empty-function.md @@ -180,7 +180,7 @@ This rule has an option to allow specific kinds of functions to be empty. * `"asyncFunctions"` - Async functions. * `"asyncMethods"` - Async class methods and method shorthands of object literals. -#### allow: functions +### allow: functions Examples of **correct** code for the `{ "allow": ["functions"] }` option: @@ -196,7 +196,7 @@ var obj = { }; ``` -#### allow: arrowFunctions +### allow: arrowFunctions Examples of **correct** code for the `{ "allow": ["arrowFunctions"] }` option: @@ -207,7 +207,7 @@ Examples of **correct** code for the `{ "allow": ["arrowFunctions"] }` option: var foo = () => {}; ``` -#### allow: generatorFunctions +### allow: generatorFunctions Examples of **correct** code for the `{ "allow": ["generatorFunctions"] }` option: @@ -224,7 +224,7 @@ var obj = { }; ``` -#### allow: methods +### allow: methods Examples of **correct** code for the `{ "allow": ["methods"] }` option: @@ -242,7 +242,7 @@ class A { } ``` -#### allow: generatorMethods +### allow: generatorMethods Examples of **correct** code for the `{ "allow": ["generatorMethods"] }` option: @@ -260,7 +260,7 @@ class A { } ``` -#### allow: getters +### allow: getters Examples of **correct** code for the `{ "allow": ["getters"] }` option: @@ -278,7 +278,7 @@ class A { } ``` -#### allow: setters +### allow: setters Examples of **correct** code for the `{ "allow": ["setters"] }` option: @@ -296,7 +296,7 @@ class A { } ``` -#### allow: constructors +### allow: constructors Examples of **correct** code for the `{ "allow": ["constructors"] }` option: @@ -309,7 +309,7 @@ class A { } ``` -#### allow: asyncFunctions +### allow: asyncFunctions Examples of **correct** code for the `{ "allow": ["asyncFunctions"] }` options: @@ -320,7 +320,7 @@ Examples of **correct** code for the `{ "allow": ["asyncFunctions"] }` options: async function a(){} ``` -#### allow: asyncMethods +### allow: asyncMethods Examples of **correct** code for the `{ "allow": ["asyncMethods"] }` options: diff --git a/eslint/docs/rules/no-empty-label.md b/eslint/docs/rules/no-empty-label.md index 3f88501..b887c87 100644 --- a/eslint/docs/rules/no-empty-label.md +++ b/eslint/docs/rules/no-empty-label.md @@ -4,7 +4,6 @@ Labeled statements are only used in conjunction with labeled break and continue statements. ECMAScript has no goto statement. - ## Rule Details This error occurs when a label is used to mark a statement that is not an iteration or switch diff --git a/eslint/docs/rules/no-implied-eval.md b/eslint/docs/rules/no-implied-eval.md index d300ff6..cbf7c67 100644 --- a/eslint/docs/rules/no-implied-eval.md +++ b/eslint/docs/rules/no-implied-eval.md @@ -19,7 +19,6 @@ setTimeout(function() { The best practice is to always use a function for the first argument of `setTimeout()` and `setInterval()` (and avoid `execScript()`). - ## Rule Details This rule aims to eliminate implied `eval()` through the use of `setTimeout()`, `setInterval()` or `execScript()`. As such, it will warn when either function is used with a string as the first argument. diff --git a/eslint/docs/rules/no-loop-func.md b/eslint/docs/rules/no-loop-func.md index 65c7532..f39b868 100644 --- a/eslint/docs/rules/no-loop-func.md +++ b/eslint/docs/rules/no-loop-func.md @@ -26,7 +26,6 @@ for (let i = 0; i < 10; i++) { In this case, each function created within the loop returns a different number as expected. - ## Rule Details This error is raised to highlight a piece of code that may not work as you expect it to and could also indicate a misunderstanding of how the language works. Your code may run without any problems if you do not fix this error, but in some situations it could behave unexpectedly. diff --git a/eslint/docs/rules/no-mixed-operators.md b/eslint/docs/rules/no-mixed-operators.md index 33ce7f6..1dca581 100644 --- a/eslint/docs/rules/no-mixed-operators.md +++ b/eslint/docs/rules/no-mixed-operators.md @@ -36,7 +36,6 @@ will generate 1:18 Unexpected mix of '&&' and '?:'. (no-mixed-operators) ``` - ## Rule Details This rule checks `BinaryExpression`, `LogicalExpression` and `ConditionalExpression`. diff --git a/eslint/docs/rules/no-mixed-spaces-and-tabs.md b/eslint/docs/rules/no-mixed-spaces-and-tabs.md index ba34dbe..9f156bb 100644 --- a/eslint/docs/rules/no-mixed-spaces-and-tabs.md +++ b/eslint/docs/rules/no-mixed-spaces-and-tabs.md @@ -59,7 +59,6 @@ function main() { } ``` - ## Further Reading * [Smart Tabs](https://www.emacswiki.org/emacs/SmartTabs) diff --git a/eslint/docs/rules/no-multiple-empty-lines.md b/eslint/docs/rules/no-multiple-empty-lines.md index aff6c2e..1a04841 100644 --- a/eslint/docs/rules/no-multiple-empty-lines.md +++ b/eslint/docs/rules/no-multiple-empty-lines.md @@ -69,7 +69,7 @@ var bar = 3; **Incorrect**: -``` +```js 1 /*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/⏎ 2 ⏎ 3 var foo = 5;⏎ @@ -82,7 +82,7 @@ var bar = 3; **Correct**: -``` +```js 1 /*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/⏎ 2 ⏎ 3 var foo = 5;⏎ diff --git a/eslint/docs/rules/no-param-reassign.md b/eslint/docs/rules/no-param-reassign.md index 47a7af3..29952c3 100644 --- a/eslint/docs/rules/no-param-reassign.md +++ b/eslint/docs/rules/no-param-reassign.md @@ -150,7 +150,6 @@ function foo(barBaz) { } ``` - ## When Not To Use It If you want to allow assignment to function parameters, then you can safely disable this rule. diff --git a/eslint/docs/rules/no-process-env.md b/eslint/docs/rules/no-process-env.md index 83bfa6d..4655e96 100644 --- a/eslint/docs/rules/no-process-env.md +++ b/eslint/docs/rules/no-process-env.md @@ -4,7 +4,6 @@ This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule The `process.env` object in Node.js is used to store deployment/configuration parameters. Littering it through out a project could lead to maintenance issues as it's another kind of global dependency. As such, it could lead to merge conflicts in a multi-user setup and deployment issues in a multi-server setup. Instead, one of the best practices is to define all those parameters in a single configuration/settings file which could be accessed throughout the project. - ## Rule Details This rule is aimed at discouraging use of `process.env` to avoid global dependencies. As such, it will warn whenever `process.env` is used. diff --git a/eslint/docs/rules/no-restricted-imports.md b/eslint/docs/rules/no-restricted-imports.md index f1577d7..d8e0610 100644 --- a/eslint/docs/rules/no-restricted-imports.md +++ b/eslint/docs/rules/no-restricted-imports.md @@ -91,7 +91,7 @@ or like this if you want to apply a custom message to pattern matches: The custom message will be appended to the default error message. -To restrict the use of all Node.js core imports (via https://github.com/nodejs/node/tree/master/lib): +To restrict the use of all Node.js core imports (via ): ```json "no-restricted-imports": ["error", diff --git a/eslint/docs/rules/no-restricted-modules.md b/eslint/docs/rules/no-restricted-modules.md index 3fc412f..3bce058 100644 --- a/eslint/docs/rules/no-restricted-modules.md +++ b/eslint/docs/rules/no-restricted-modules.md @@ -57,8 +57,7 @@ or like this: The custom message will be appended to the default error message. Please note that you may not specify custom error messages for restricted patterns as a particular module may match more than one pattern. - -To restrict the use of all Node.js core modules (via https://github.com/nodejs/node/tree/master/lib): +To restrict the use of all Node.js core modules (via ): ```json { diff --git a/eslint/docs/rules/no-restricted-properties.md b/eslint/docs/rules/no-restricted-properties.md index e1824d5..468d0b3 100644 --- a/eslint/docs/rules/no-restricted-properties.md +++ b/eslint/docs/rules/no-restricted-properties.md @@ -2,7 +2,6 @@ Certain properties on objects may be disallowed in a codebase. This is useful for deprecating an API or restricting usage of a module's methods. For example, you may want to disallow using `describe.only` when using Mocha or telling people to use `Object.assign` instead of `_.extend`. - ## Rule Details This rule looks for accessing a given property key on a given object name, either when reading the property's value or invoking it as a function. You may specify an optional message to indicate an alternative API or a reason for the restriction. diff --git a/eslint/docs/rules/no-template-curly-in-string.md b/eslint/docs/rules/no-template-curly-in-string.md index 9c03b67..68a237d 100644 --- a/eslint/docs/rules/no-template-curly-in-string.md +++ b/eslint/docs/rules/no-template-curly-in-string.md @@ -2,7 +2,6 @@ ECMAScript 6 allows programmers to create strings containing variable or expressions using template literals, instead of string concatenation, by writing expressions like `${variable}` between two backtick quotes (\`). It can be easy to use the wrong quotes when wanting to use template literals, by writing `"${variable}"`, and end up with the literal value `"${variable}"` instead of a string containing the value of the injected expressions. - ## Rule Details This rule aims to warn when a regular string contains what looks like a template literal placeholder. It will warn when it finds a string containing the template literal placeholder (`${something}`) that uses either `"` or `'` for the quotes. diff --git a/eslint/docs/rules/no-undef-init.md b/eslint/docs/rules/no-undef-init.md index 0cf99c1..10bde77 100644 --- a/eslint/docs/rules/no-undef-init.md +++ b/eslint/docs/rules/no-undef-init.md @@ -16,7 +16,6 @@ var foo = undefined; It's considered a best practice to avoid initializing variables to `undefined`. - ## Rule Details This rule aims to eliminate `var` and `let` variable declarations that initialize to `undefined`. diff --git a/eslint/docs/rules/no-undefined.md b/eslint/docs/rules/no-undefined.md index 0e183f2..0e67c21 100644 --- a/eslint/docs/rules/no-undefined.md +++ b/eslint/docs/rules/no-undefined.md @@ -22,7 +22,6 @@ Because `undefined` can be overwritten or shadowed, reading `undefined` can give As an alternative, you can use the [no-global-assign](no-global-assign.md) and [no-shadow-restricted-names](no-shadow-restricted-names.md) rules to prevent `undefined` from being shadowed or assigned a different value. This ensures that `undefined` will always hold its original, expected value. - ## Rule Details This rule aims to eliminate the use of `undefined`, and as such, generates a warning whenever it is used. diff --git a/eslint/docs/rules/no-unused-vars.md b/eslint/docs/rules/no-unused-vars.md index 3064703..24eb337 100644 --- a/eslint/docs/rules/no-unused-vars.md +++ b/eslint/docs/rules/no-unused-vars.md @@ -293,7 +293,6 @@ try { } ``` - ## When Not To Use It If you don't want to be notified about unused variables or function arguments, you can safely turn this rule off. diff --git a/eslint/docs/rules/no-void.md b/eslint/docs/rules/no-void.md index 6b77748..3f78376 100644 --- a/eslint/docs/rules/no-void.md +++ b/eslint/docs/rules/no-void.md @@ -38,7 +38,7 @@ void function(){ foo = 1; }() // will assign foo a value of 1 +function(){ foo = 1; }() // same as above ``` -``` +```js function(){ foo = 1; }() // will throw SyntaxError ``` diff --git a/eslint/docs/rules/no-wrap-func.md b/eslint/docs/rules/no-wrap-func.md index 6732c92..5193d79 100644 --- a/eslint/docs/rules/no-wrap-func.md +++ b/eslint/docs/rules/no-wrap-func.md @@ -2,7 +2,6 @@ (removed) This rule was **removed** in ESLint v1.0 and **replaced** by the [no-extra-parens](no-extra-parens.md) rule. The `"functions"` option in the new rule is equivalent to the removed rule. - Although it's possible to wrap functions in parentheses, this can be confusing when the code also contains immediately-invoked function expressions (IIFEs) since parentheses are often used to make this distinction. For example: ```js diff --git a/eslint/docs/rules/object-shorthand.md b/eslint/docs/rules/object-shorthand.md index 79e7665..fee0e62 100644 --- a/eslint/docs/rules/object-shorthand.md +++ b/eslint/docs/rules/object-shorthand.md @@ -43,7 +43,6 @@ properties defined where the key name matches name of the assigned variable. Each of the following properties would warn: - ```js /*eslint object-shorthand: "error"*/ /*eslint-env es6*/ diff --git a/eslint/docs/rules/prefer-destructuring.md b/eslint/docs/rules/prefer-destructuring.md index 64e914f..b0f887c 100644 --- a/eslint/docs/rules/prefer-destructuring.md +++ b/eslint/docs/rules/prefer-destructuring.md @@ -179,7 +179,6 @@ var foo = $('body')[0]; var [bar] = $('body'); // fails with a TypeError ``` - ## Further Reading If you want to learn more about destructuring, check out the links below: diff --git a/eslint/docs/rules/prefer-named-capture-group.md b/eslint/docs/rules/prefer-named-capture-group.md index 2caf951..f48ec80 100644 --- a/eslint/docs/rules/prefer-named-capture-group.md +++ b/eslint/docs/rules/prefer-named-capture-group.md @@ -1,6 +1,5 @@ # Suggest using named capture group in regular expression (prefer-named-capture-group) - ## Rule Details With the landing of ECMAScript 2018, named capture groups can be used in regular expressions, which can improve their readability. diff --git a/eslint/docs/rules/prefer-promise-reject-errors.md b/eslint/docs/rules/prefer-promise-reject-errors.md index ac0a227..9740bc9 100644 --- a/eslint/docs/rules/prefer-promise-reject-errors.md +++ b/eslint/docs/rules/prefer-promise-reject-errors.md @@ -2,7 +2,6 @@ It is considered good practice to only pass instances of the built-in `Error` object to the `reject()` function for user-defined errors in Promises. `Error` objects automatically store a stack trace, which can be used to debug an error by determining where it came from. If a Promise is rejected with a non-`Error` value, it can be difficult to determine where the rejection occurred. - ## Rule Details This rule aims to ensure that Promises are only rejected with `Error` objects. diff --git a/eslint/docs/rules/prefer-reflect.md b/eslint/docs/rules/prefer-reflect.md index 55bc1bb..af0838e 100644 --- a/eslint/docs/rules/prefer-reflect.md +++ b/eslint/docs/rules/prefer-reflect.md @@ -19,7 +19,7 @@ The prefer-reflect rule will flag usage of any older method, suggesting to inste ### Exceptions -``` +```js "prefer-reflect": [, { "exceptions": [<...exceptions>] }] ``` diff --git a/eslint/docs/rules/radix.md b/eslint/docs/rules/radix.md index da3196c..4453a64 100644 --- a/eslint/docs/rules/radix.md +++ b/eslint/docs/rules/radix.md @@ -29,7 +29,6 @@ There are two options for this rule: * `"always"` enforces providing a radix (default) * `"as-needed"` disallows providing the `10` radix - ### always Examples of **incorrect** code for the default `"always"` option: diff --git a/eslint/docs/rules/require-await.md b/eslint/docs/rules/require-await.md index 595d0ac..9c84d43 100644 --- a/eslint/docs/rules/require-await.md +++ b/eslint/docs/rules/require-await.md @@ -81,7 +81,6 @@ In this case, the `fail()` function throws an error that is intended to be caugh If you are throwing an error inside of an asynchronous function for this purpose, then you may want to disable this rule. - ## Related Rules * [require-yield](require-yield.md) diff --git a/eslint/docs/rules/sort-imports.md b/eslint/docs/rules/sort-imports.md index 637c9fc..08c2cf3 100644 --- a/eslint/docs/rules/sort-imports.md +++ b/eslint/docs/rules/sort-imports.md @@ -22,7 +22,6 @@ import "my-module.js" When declaring multiple imports, a sorted list of import declarations make it easier for developers to read the code and find necessary imports later. This rule is purely a matter of style. - ## Rule Details This rule checks all import declarations and verifies that all imports are first sorted by the used member syntax and then alphabetically by the first member or alias name. diff --git a/eslint/docs/rules/space-after-function-name.md b/eslint/docs/rules/space-after-function-name.md index 4607ff4..6cceaab 100644 --- a/eslint/docs/rules/space-after-function-name.md +++ b/eslint/docs/rules/space-after-function-name.md @@ -20,7 +20,6 @@ Some style guides may require a consistent spacing for function names. This rule aims to enforce a consistent spacing after function names. It takes one argument. If it is `"always"` then all function names must be followed by at least one space. If `"never"` then there should be no spaces between the name and the parameter list. The default is `"never"`. - Examples of **incorrect** code for this rule: ```js diff --git a/eslint/docs/rules/space-in-brackets.md b/eslint/docs/rules/space-in-brackets.md index 9bb2477..330609a 100644 --- a/eslint/docs/rules/space-in-brackets.md +++ b/eslint/docs/rules/space-in-brackets.md @@ -232,7 +232,6 @@ var arr = [ { Examples of **correct** code when `"objectsInArrays"` is set to `false`: - ```js var arr = [{ 'foo': 'bar' }]; var arr = [{ diff --git a/eslint/docs/rules/spaced-comment.md b/eslint/docs/rules/spaced-comment.md index 5a885f9..66f8f70 100644 --- a/eslint/docs/rules/spaced-comment.md +++ b/eslint/docs/rules/spaced-comment.md @@ -24,7 +24,7 @@ The rule takes two options. * The `"exceptions"` value is an array of string patterns which are considered exceptions to the rule. The rule will not warn when the pattern starts from the beginning of the comment and repeats until the end of the line or `*/` if the comment is a single line comment. Please note that exceptions are ignored if the first argument is `"never"`. - ``` + ```js "spaced-comment": ["error", "always", { "exceptions": ["-", "+"] }] ``` @@ -32,7 +32,7 @@ The rule takes two options. such as an additional `/`, used to denote documentation read by doxygen, vsdoc, etc. which must have additional characters. The `"markers"` array will apply regardless of the value of the first argument, e.g. `"always"` or `"never"`. - ``` + ```js "spaced-comment": ["error", "always", { "markers": ["/"] }] ``` @@ -281,7 +281,6 @@ subsequent lines are ignored /*global ABC*/ ``` - ## Related Rules * [spaced-line-comment](spaced-line-comment.md) diff --git a/eslint/docs/rules/spaced-line-comment.md b/eslint/docs/rules/spaced-line-comment.md index b50404b..aaaa27a 100644 --- a/eslint/docs/rules/spaced-line-comment.md +++ b/eslint/docs/rules/spaced-line-comment.md @@ -6,7 +6,6 @@ Some style guides require or disallow a whitespace immediately after the initial Whitespace after the `//` makes it easier to read text in comments. On the other hand, commenting out code is easier without having to put a whitespace right after the `//`. - ## Rule Details This rule will enforce consistency of spacing after the start of a line comment `//`. diff --git a/eslint/docs/rules/switch-colon-spacing.md b/eslint/docs/rules/switch-colon-spacing.md index bd469ea..b9872c2 100644 --- a/eslint/docs/rules/switch-colon-spacing.md +++ b/eslint/docs/rules/switch-colon-spacing.md @@ -20,7 +20,6 @@ This rule has 2 options that are boolean value. - `"before": true` requires one or more spaces before colons. - `"before": false` (Default) disallows before colons. - Examples of **incorrect** code for this rule: ```js diff --git a/eslint/docs/rules/symbol-description.md b/eslint/docs/rules/symbol-description.md index 61be876..7743867 100644 --- a/eslint/docs/rules/symbol-description.md +++ b/eslint/docs/rules/symbol-description.md @@ -9,7 +9,6 @@ var someString = "some description"; var bar = Symbol(someString); ``` - Using `description` promotes easier debugging: when a symbol is logged the description is used: ```js @@ -21,12 +20,10 @@ var foo = Symbol("some description"); It may facilitate identifying symbols when one is observed during debugging. - ## Rule Details This rules requires a description when creating symbols. - ## Examples Examples of **incorrect** code for this rule: @@ -50,7 +47,6 @@ var someString = "some description"; var bar = Symbol(someString); ``` - ## When Not To Use It This rule should not be used in ES3/5 environments. diff --git a/eslint/docs/user-guide/command-line-interface.md b/eslint/docs/user-guide/command-line-interface.md index ad84c37..6f350d4 100644 --- a/eslint/docs/user-guide/command-line-interface.md +++ b/eslint/docs/user-guide/command-line-interface.md @@ -1,6 +1,6 @@ # Command Line Interface -To run ESLint on Node.js, you must have npm installed. If npm is not installed, follow the instructions here: https://www.npmjs.com/ +To run ESLint on Node.js, you must have npm installed. If npm is not installed, follow the instructions here: Once npm is installed, run the following @@ -30,62 +30,62 @@ The command line utility has several options. You can view the options by runnin eslint [options] file.js [file.js] [dir] Basic configuration: - --no-eslintrc Disable use of configuration from .eslintrc.* - -c, --config path::String Use this configuration, overriding .eslintrc.* config options if present - --env [String] Specify environments - --ext [String] Specify JavaScript file extensions - default: .js - --global [String] Define global variables - --parser String Specify the parser to be used - --parser-options Object Specify parser options + --no-eslintrc Disable use of configuration from .eslintrc.* + -c, --config path::String Use this configuration, overriding .eslintrc.* config options if present + --env [String] Specify environments + --ext [String] Specify JavaScript file extensions + --global [String] Define global variables + --parser String Specify the parser to be used + --parser-options Object Specify parser options --resolve-plugins-relative-to path::String A folder where plugins should be resolved from, CWD by default Specifying rules and plugins: - --rulesdir [path::String] Use additional rules from this directory - --plugin [String] Specify plugins - --rule Object Specify rules + --plugin [String] Specify plugins + --rule Object Specify rules + --rulesdir [path::String] Load additional rules from this directory. Deprecated: Use rules from plugins Fixing problems: - --fix Automatically fix problems - --fix-dry-run Automatically fix problems without saving the changes to the file system - --fix-type Array Specify the types of fixes to apply (directive, problem, suggestion, layout) + --fix Automatically fix problems + --fix-dry-run Automatically fix problems without saving the changes to the file system + --fix-type Array Specify the types of fixes to apply (directive, problem, suggestion, layout) Ignoring files: - --ignore-path path::String Specify path of ignore file - --no-ignore Disable use of ignore files and patterns - --ignore-pattern [String] Pattern of files to ignore (in addition to those in .eslintignore) + --ignore-path path::String Specify path of ignore file + --no-ignore Disable use of ignore files and patterns + --ignore-pattern [String] Pattern of files to ignore (in addition to those in .eslintignore) Using stdin: - --stdin Lint code provided on - default: false - --stdin-filename String Specify filename to process STDIN as + --stdin Lint code provided on - default: false + --stdin-filename String Specify filename to process STDIN as Handling warnings: - --quiet Report errors only - default: false - --max-warnings Int Number of warnings to trigger nonzero exit code - default: -1 + --quiet Report errors only - default: false + --max-warnings Int Number of warnings to trigger nonzero exit code - default: -1 Output: -o, --output-file path::String Specify file to write report to - -f, --format String Use a specific output format - default: stylish - --color, --no-color Force enabling/disabling of color + -f, --format String Use a specific output format - default: stylish + --color, --no-color Force enabling/disabling of color Inline configuration comments: - --no-inline-config Prevent comments from changing config or rules + --no-inline-config Prevent comments from changing config or rules --report-unused-disable-directives Adds reported errors for unused eslint-disable directives Caching: - --cache Only check changed files - default: false - --cache-file path::String Path to the cache file. Deprecated: use --cache-location - default: .eslintcache - --cache-location path::String Path to the cache file or directory - --cache-strategy String Strategy to use for detecting changed files - either: metadata or content - default: metadata + --cache Only check changed files - default: false + --cache-file path::String Path to the cache file. Deprecated: use --cache-location - default: .eslintcache + --cache-location path::String Path to the cache file or directory + --cache-strategy String Strategy to use for detecting changed files in the cache - either: metadata or content - default: metadata Miscellaneous: - --init Run config initialization wizard - default: false - --env-info Output execution environment information - default: false - --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched - default: false - --exit-on-fatal-error Exit with exit code 2 in case of fatal error - default: false - --debug Output debugging information - -h, --help Show help - -v, --version Output the version number - --print-config path::String Print the configuration for the given file + --init Run config initialization wizard - default: false + --env-info Output execution environment information - default: false + --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched + --exit-on-fatal-error Exit with exit code 2 in case of fatal error - default: false + --debug Output debugging information + -h, --help Show help + -v, --version Output the version number + --print-config path::String Print the configuration for the given file ``` Options that accept array values can be specified by repeating the option or with a comma-delimited list (other than `--ignore-pattern` which does not allow the second style). @@ -178,20 +178,6 @@ Changes the folder where plugins are resolved from. By default, plugins are reso ### Specifying rules and plugins -#### `--rulesdir` - -This option allows you to specify another directory from which to load rules files. This allows you to dynamically load new rules at run time. This is useful when you have custom rules that aren't suitable for being bundled with ESLint. - -Example: - - eslint --rulesdir my-rules/ file.js - -The rules in your custom rules directory must follow the same format as bundled rules to work properly. You can also specify multiple locations for custom rules by including multiple `--rulesdir` options: - - eslint --rulesdir my-rules/ --rulesdir my-other-rules/ file.js - -Note that, as with core rules and plugin rules, you still need to enable the rules in configuration or via the `--rule` CLI option in order to actually run those rules during linting. Specifying a rules directory with `--rulesdir` does not automatically enable the rules within that directory. - #### `--plugin` This option specifies a plugin to load. You can omit the prefix `eslint-plugin-` from the plugin name. @@ -215,6 +201,22 @@ Examples: eslint --rule 'guard-for-in: error' --rule 'brace-style: [error, 1tbs]' eslint --rule 'jquery/dollar-sign: error' +#### `--rulesdir` + +**Deprecated**: Use rules from plugins instead. + +This option allows you to specify another directory from which to load rules files. This allows you to dynamically load new rules at run time. This is useful when you have custom rules that aren't suitable for being bundled with ESLint. + +Example: + + eslint --rulesdir my-rules/ file.js + +The rules in your custom rules directory must follow the same format as bundled rules to work properly. You can also specify multiple locations for custom rules by including multiple `--rulesdir` options: + + eslint --rulesdir my-rules/ --rulesdir my-other-rules/ file.js + +Note that, as with core rules and plugin rules, you still need to enable the rules in configuration or via the `--rule` CLI option in order to actually run those rules during linting. Specifying a rules directory with `--rulesdir` does not automatically enable the rules within that directory. + ### Fixing problems #### `--fix` @@ -232,7 +234,7 @@ This option has the same effect as `--fix` with one difference: the fixes are no Because the default formatter does not output the fixed code, you'll have to use another one (e.g. `json`) to get the fixes. Here's an example of this pattern: -``` +```sh getSomeText | eslint --stdin --fix-dry-run --format=json ``` @@ -249,7 +251,7 @@ This option allows you to specify the type of fixes to apply when using either ` You can specify one or more fix type on the command line. Here are some examples: -``` +```sh eslint --fix --fix-type suggestion . eslint --fix --fix-type suggestion --fix-type problem . eslint --fix --fix-type suggestion,layout . @@ -473,7 +475,7 @@ This option causes ESLint to exit with exit code 2 if one or more fatal parsing #### `--debug` -This option outputs debugging information to the console. This information is useful when you're seeing a problem and having a hard time pinpointing it. The ESLint team may ask for this debugging information to help solve bugs. +This option outputs debugging information to the console. This information is useful when you're seeing a problem and having a hard time pinpointing it. The ESLint team may ask for this debugging information to help solve bugs. Add this flag to an ESLint command line invocation in order to get extra debug information as the command is run (e.g. `eslint --debug test.js` and `eslint test.js --debug` are equivalent) #### `-h`, `--help` diff --git a/eslint/docs/user-guide/configuring/configuration-files.md b/eslint/docs/user-guide/configuring/configuration-files.md index 091d043..b1fded2 100644 --- a/eslint/docs/user-guide/configuring/configuration-files.md +++ b/eslint/docs/user-guide/configuring/configuration-files.md @@ -392,7 +392,7 @@ Here is how overrides work in a configuration file: ### Relative glob patterns -``` +```pt project-root ├── app │ ├── lib diff --git a/eslint/docs/user-guide/configuring/ignoring-code.md b/eslint/docs/user-guide/configuring/ignoring-code.md index d7b3cfd..35c2d25 100644 --- a/eslint/docs/user-guide/configuring/ignoring-code.md +++ b/eslint/docs/user-guide/configuring/ignoring-code.md @@ -1,6 +1,5 @@ # Ignoring Code - * [`ignorePatterns` in Config Files](#ignorepatterns-in-config-files) * [The `.eslintignore` File](#the-eslintignore-file) * [Using an Alternate File](#using-an-alternate-file) diff --git a/eslint/docs/user-guide/configuring/language-options.md b/eslint/docs/user-guide/configuring/language-options.md index 1ee3f2d..d44959d 100644 --- a/eslint/docs/user-guide/configuring/language-options.md +++ b/eslint/docs/user-guide/configuring/language-options.md @@ -1,6 +1,5 @@ # Language Options - * [Specifying Environments](#specifying-environments) * [Specifying Globals](#specifying-globals) * [Specifying Parser Options](#specifying-parser-options) @@ -174,7 +173,6 @@ Globals can be disabled with the string `"off"`. For example, in an environment For historical reasons, the boolean value `false` and the string value `"readable"` are equivalent to `"readonly"`. Similarly, the boolean value `true` and the string value `"writeable"` are equivalent to `"writable"`. However, the use of older values is deprecated. - ## Specifying Parser Options ESLint allows you to specify the JavaScript language options you want to support. By default, ESLint expects ECMAScript 5 syntax. You can override that setting to enable support for other ECMAScript versions as well as JSX by using parser options. @@ -189,6 +187,7 @@ Parser options are set in your `.eslintrc.*` file by using the `parserOptions` p * `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, 9, 10, 11, 12, or 13 to specify the version of ECMAScript syntax you want to use. You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), 2019 (same as 10), 2020 (same as 11), 2021 (same as 12), or 2022 (same as 13) to use the year-based naming. You can also set "latest" to use the most recently supported version. * `sourceType` - set to `"script"` (default) or `"module"` if your code is in ECMAScript modules. +* `allowReserved` - allow the use of reserved words as identifiers (if `ecmaVersion` is 3). * `ecmaFeatures` - an object indicating which additional language features you'd like to use: * `globalReturn` - allow `return` statements in the global scope * `impliedStrict` - enable global [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) (if `ecmaVersion` is 5 or greater) diff --git a/eslint/docs/user-guide/configuring/plugins.md b/eslint/docs/user-guide/configuring/plugins.md index 0235586..9d2c929 100644 --- a/eslint/docs/user-guide/configuring/plugins.md +++ b/eslint/docs/user-guide/configuring/plugins.md @@ -1,6 +1,5 @@ # Plugins - * [Specifying Parser](#specifying-parser) * [Specifying Processor](#specifying-processor) * [Configuring Plugins](#configuring-plugins) diff --git a/eslint/docs/user-guide/configuring/rules.md b/eslint/docs/user-guide/configuring/rules.md index 0191f63..4f45b4a 100644 --- a/eslint/docs/user-guide/configuring/rules.md +++ b/eslint/docs/user-guide/configuring/rules.md @@ -1,6 +1,5 @@ # Rules - * [Configuring Rules](#configuring-rules) * [Disabling Rules](#disabling-rules) diff --git a/eslint/docs/user-guide/getting-started.md b/eslint/docs/user-guide/getting-started.md index 3c9633b..cf808dd 100644 --- a/eslint/docs/user-guide/getting-started.md +++ b/eslint/docs/user-guide/getting-started.md @@ -12,7 +12,7 @@ Prerequisites: [Node.js](https://nodejs.org/en/) (`^12.22.0`, `^14.17.0`, or `>= You can install ESLint using npm or yarn: -``` +```sh npm install eslint --save-dev # or @@ -22,7 +22,7 @@ yarn add eslint --dev You should then set up a configuration file, and the easiest way to do that is to use the `--init` flag: -``` +```sh $ npx eslint --init # or @@ -34,7 +34,7 @@ $ yarn run eslint --init After that, you can run ESLint on any file or directory like this: -``` +```sh $ npx eslint yourfile.js # or diff --git a/eslint/docs/user-guide/integrations.md b/eslint/docs/user-guide/integrations.md index c919c2d..f7680be 100644 --- a/eslint/docs/user-guide/integrations.md +++ b/eslint/docs/user-guide/integrations.md @@ -15,7 +15,7 @@ * [eslint.tmbundle](https://github.com/ryanfitzer/eslint.tmbundle) * [javascript-eslint.tmbundle](https://github.com/natesilva/javascript-eslint.tmbundle) * Atom: [linter-eslint](https://atom.io/packages/linter-eslint) -* IntelliJ IDEA, RubyMine, WebStorm, PhpStorm, PyCharm, AppCode, Android Studio, 0xDBE: [ESLint Plugin](https://plugins.jetbrains.com/plugin/7494-eslint) +* IntelliJ IDEA, WebStorm, PhpStorm, PyCharm, RubyMine, and other JetBrains IDEs: [How to use ESLint](https://www.jetbrains.com/help/webstorm/eslint.html) * Visual Studio Code: [ESLint Extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) * Brackets: Included and [Brackets ESLint](https://github.com/brackets-userland/brackets-eslint) diff --git a/eslint/docs/user-guide/migrating-from-jscs.md b/eslint/docs/user-guide/migrating-from-jscs.md index c5ac5f2..e05ee70 100644 --- a/eslint/docs/user-guide/migrating-from-jscs.md +++ b/eslint/docs/user-guide/migrating-from-jscs.md @@ -15,7 +15,7 @@ Before beginning the process of migrating to ESLint, it's helpful to understand To install Polyjuice: -``` +```sh $ npm install -g polyjuice ``` @@ -23,7 +23,7 @@ Polyjuice works with JSON configuration files, so if you're using a JavaScript o To convert your configuration file, pass in the location of your `.jscs.json` file using the `--jscs` flag: -``` +```sh $ polyjuice --jscs .jscsrc.json > .eslintrc.json ``` @@ -31,7 +31,7 @@ This creates a `.eslintrc.json` with the equivalent rules from `.jscsrc.json`. If you have multiple `.jscsrc.json` files, you can pass them all and Polyjuice will combine them into one `.eslintrc.json` file: -``` +```sh $ polyjuice --jscs .jscsrc.json ./foo/.jscsrc.json > .eslintrc.json ``` @@ -41,7 +41,7 @@ $ polyjuice --jscs .jscsrc.json ./foo/.jscsrc.json > .eslintrc.json If you don't want to convert your JSCS configuration directly into an ESLint configuration, then you can use ESLint's built-in wizard to get you started. Just run: -``` +```sh $ eslint --init ``` @@ -74,7 +74,7 @@ As an example, suppose that you are using the `airbnb` preset, so your `.jscsrc` In order to get the same functionality in ESLint, you would first need to install the `eslint-config-airbnb` shareable config package: -``` +```sh $ npm install eslint-config-airbnb-base --save-dev ``` @@ -110,13 +110,13 @@ Both JSCS and ESLint have command line arguments corresponding to many of their JSCS uses the `--fix` option to apply automatic fixes to code: -``` +```sh $ jscs --fix file.js ``` ESLint has the same option: -``` +```sh $ eslint --fix file.js ``` @@ -124,13 +124,13 @@ $ eslint --fix file.js The JSCS `--auto-configure` option created a configuration based on what it found in a given file: -``` +```sh $ jscs --auto-configure file.js ``` In ESLint, there's a similar option when you use `--init`. Just select "Inspect your JavaScript file(s)": -``` +```sh $ eslint --init ? How would you like to configure ESLint? (Use arrow keys) > Answer questions about your style @@ -142,30 +142,28 @@ $ eslint --init JSCS allows you to specify a configuration file to use on the command line using either `--config` or `-c`, such as: -``` +```sh $ jscs --config myconfig.json file.js $ jscs -c myconfig.json file.js ``` Both flags are also supported by ESLint: -``` +```sh $ eslint --config myconfig.json file.js $ eslint -c myconfig.json file.js ``` - - ## Piping Code Into ESLint In JSCS, you can pipe code in like this: -``` +```sh $ cat file.js | jscs ``` In ESLint, you can also pipe in code, but you need to use the `--stdin` flag: -``` +```sh $ cat file.js | eslint --stdin ``` diff --git a/eslint/docs/user-guide/migrating-to-2.0.0.md b/eslint/docs/user-guide/migrating-to-2.0.0.md index 95a3a17..c5f50b3 100644 --- a/eslint/docs/user-guide/migrating-to-2.0.0.md +++ b/eslint/docs/user-guide/migrating-to-2.0.0.md @@ -285,7 +285,7 @@ if (variable) { } ``` -Further Reading: https://estools.github.io/escope/ +Further Reading: ## Default Changes When Using `eslint:recommended` diff --git a/eslint/docs/user-guide/migrating-to-4.0.0.md b/eslint/docs/user-guide/migrating-to-4.0.0.md index b62ef05..8f78291 100644 --- a/eslint/docs/user-guide/migrating-to-4.0.0.md +++ b/eslint/docs/user-guide/migrating-to-4.0.0.md @@ -4,7 +4,7 @@ ESLint v4.0.0 is the fourth major version release. We have made several breaking The lists below are ordered roughly by the number of users each change is expected to affect, where the first items are expected to affect the most users. -### Breaking changes for users +## Breaking changes for users 1. [New rules have been added to `eslint:recommended`](#eslint-recommended-changes) 1. [The `indent` rule is more strict](#indent-rewrite) @@ -15,14 +15,14 @@ The lists below are ordered roughly by the number of users each change is expect 1. [The `no-multi-spaces` rule is more strict by default](#no-multi-spaces-eol-comments) 1. [References to scoped plugins in config files are now required to include the scope](#scoped-plugin-resolution) -### Breaking changes for plugin/custom rule developers +## Breaking changes for plugin/custom rule developers 1. [`RuleTester` now validates properties of test cases](#rule-tester-validation) 1. [AST nodes no longer have comment properties](#comment-attachment) 1. [`LineComment` and `BlockComment` events will no longer be emitted during AST traversal](#event-comments) 1. [Shebangs are now returned from comment APIs](#shebangs) -### Breaking changes for integration developers +## Breaking changes for integration developers 1. [The `global` property in the `linter.verify()` API is no longer supported](#global-property) 1. [More report messages now have full location ranges](#report-locations) @@ -186,7 +186,7 @@ Starting in 4.0, `LineComment` and `BlockComments` events will not be emitted du **To address:** Instead of relying on `LineComment` and `BlockComment`, rules can now use `sourceCode.getAllComments()` to get all comments in a file. To check all comments of a specific type, rules can use the following pattern: -``` +```js sourceCode.getAllComments().filter(comment => comment.type === "Line"); sourceCode.getAllComments().filter(comment => comment.type === "Block"); ``` @@ -199,7 +199,7 @@ In 4.0, shebang comments are treated as comment tokens of type `Shebang` and wil **To address:** If you have a custom rule that performs operations on comments, some additional logic might be required to ensure that shebang comments are correctly handled or filtered out: -``` +```js sourceCode.getAllComments().filter(comment => comment.type !== "Shebang"); ``` diff --git a/eslint/docs/user-guide/migrating-to-5.0.0.md b/eslint/docs/user-guide/migrating-to-5.0.0.md index da5c435..3729631 100644 --- a/eslint/docs/user-guide/migrating-to-5.0.0.md +++ b/eslint/docs/user-guide/migrating-to-5.0.0.md @@ -4,7 +4,7 @@ ESLint v5.0.0 is the fifth major version release. We have made a few breaking ch The lists below are ordered roughly by the number of users each change is expected to affect, where the first items are expected to affect the most users. -### Breaking changes for users +## Breaking changes for users 1. [Node.js 4 is no longer supported](#drop-node-4) 1. [New rules have been added to `eslint:recommended`](#eslint-recommended-changes) @@ -16,7 +16,7 @@ The lists below are ordered roughly by the number of users each change is expect 1. [Plugins in scoped packages are now resolvable in configs](#scoped-plugins) 1. [Multi-line `eslint-disable-line` directives are now reported as problems](#multiline-directives) -### Breaking changes for plugin/custom rule developers +## Breaking changes for plugin/custom rule developers 1. [The `parent` property of AST nodes is now set before rules start running](#parent-before-rules) 1. [When using the default parser, spread operators now have type `SpreadElement`](#spread-operators) @@ -27,7 +27,7 @@ The lists below are ordered roughly by the number of users each change is expect 1. [`RuleTester` now uses strict equality checks in its assertions](#rule-tester-equality) 1. [Rules are now required to provide messages along with reports](#required-report-messages) -### Breaking changes for integration developers +## Breaking changes for integration developers 1. [The `source` property is no longer available on individual linting messages](#source-property) 1. [Fatal errors now result in an exit code of 2](#exit-code-two) diff --git a/eslint/docs/user-guide/migrating-to-6.0.0.md b/eslint/docs/user-guide/migrating-to-6.0.0.md index 3baec19..225cc4c 100644 --- a/eslint/docs/user-guide/migrating-to-6.0.0.md +++ b/eslint/docs/user-guide/migrating-to-6.0.0.md @@ -4,7 +4,7 @@ ESLint v6.0.0 is a major release of ESLint. We have made a few breaking changes The lists below are ordered roughly by the number of users each change is expected to affect, where the first items are expected to affect the most users. -### Breaking changes for users +## Breaking changes for users 1. [Node.js 6 is no longer supported](#drop-node-6) 1. [`eslint:recommended` has been updated](#eslint-recommended-changes) @@ -20,14 +20,14 @@ The lists below are ordered roughly by the number of users each change is expect 1. [The deprecated `experimentalObjectRestSpread` option has been removed](#experimental-object-rest-spread) 1. [User-provided regular expressions in rule options are parsed with the unicode flag](#unicode-regexes) -### Breaking changes for plugin/custom rule developers +## Breaking changes for plugin/custom rule developers 1. [Plugin authors may need to update installation instructions](#plugin-documentation) 1. [`RuleTester` now validates against invalid `default` keywords in rule schemas](#rule-tester-defaults) 1. [`RuleTester` now requires an absolute path on `parser` option](#rule-tester-parser) 1. [The `eslintExplicitGlobalComment` scope analysis property has been removed](#eslintExplicitGlobalComment) -### Breaking changes for integration developers +## Breaking changes for integration developers 1. [Plugins and shareable configs are no longer affected by ESLint's location](#package-loading-simplification) 1. [`Linter` no longer tries to load missing parsers from the filesystem](#linter-parsers) diff --git a/eslint/docs/user-guide/migrating-to-8.0.0.md b/eslint/docs/user-guide/migrating-to-8.0.0.md index 351309c..444ac12 100644 --- a/eslint/docs/user-guide/migrating-to-8.0.0.md +++ b/eslint/docs/user-guide/migrating-to-8.0.0.md @@ -51,7 +51,6 @@ ESLint v8.0.0 has removed the `codeframe` and `table` formatters from the core. **Related issue(s):** [#14277](https://github.com/eslint/eslint/issues/14277), [#14316](https://github.com/eslint/eslint/pull/14316) - ## `comma-dangle` rule schema is stricter In ESLint v7.0.0, the `comma-dangle` rule could be configured like this without error: @@ -105,7 +104,6 @@ Four new rules have been enabled in the `eslint:recommended` preset. **Related issue(s):** [#14673](https://github.com/eslint/eslint/issues/14673) - ## Rules require `meta.hasSuggestions` to provide suggestions In ESLint v7.0.0, rules that [provided suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions) did not need to let ESLint know. In v8.0.0, rules providing suggestions need to set their `meta.hasSuggestions` to `true`. This informs ESLint that the rule intends to provide suggestions. Without this property, any attempt to provide a suggestion will result in an error. @@ -219,7 +217,6 @@ In ESLint v8.0.0 (via Acorn v8.0.0), the key and value are now separate objects **Related issue(s):** [#14591](https://github.com/eslint/eslint/pull/14591#issuecomment-887733070) - ## The `CLIEngine` class has been removed The `CLIEngine` class has been removed and replaced by the [`ESLint` class](https://eslint.org/docs/developer-guide/nodejs-api#eslint-class). diff --git a/eslint/karma.conf.js b/eslint/karma.conf.js index 05f1a1c..606d13f 100644 --- a/eslint/karma.conf.js +++ b/eslint/karma.conf.js @@ -2,7 +2,7 @@ const os = require("os"); const NodePolyfillPlugin = require("node-polyfill-webpack-plugin"); -if (os.arch() === "arm64") { +if (os.platform === "linux" && os.arch() === "arm64") { // For arm64 architecture, install chromium-browser using "apt-get install chromium-browser" process.env.CHROME_BIN = "/usr/bin/chromium-browser"; @@ -16,6 +16,15 @@ module.exports = function(config) { // base path that will be used to resolve all patterns (eg. files, exclude) basePath: "", + // next three sections allow console.log to work + client: { + captureConsole: true + }, + + browserConsoleLogOptions: { + terminal: true, + level: "log" + }, /* * frameworks to use diff --git a/eslint/lib/cli.js b/eslint/lib/cli.js index 477310d..f09d143 100644 --- a/eslint/lib/cli.js +++ b/eslint/lib/cli.js @@ -178,7 +178,7 @@ async function printResults(engine, results, format, outputFile) { return false; } - const output = formatter.format(results); + const output = await formatter.format(results); if (output) { if (outputFile) { diff --git a/eslint/lib/config/default-config.js b/eslint/lib/config/default-config.js index cb6f403..a655a6d 100644 --- a/eslint/lib/config/default-config.js +++ b/eslint/lib/config/default-config.js @@ -26,7 +26,7 @@ exports.defaultConfig = [ /* * Because we try to delay loading rules until absolutely - * necessary, a proxy allows us to hook into the lazy-loading + * necessary, a proxy allows us to hook into the lazy-loading * aspect of the rules map while still keeping all of the * relevant configuration inside of the config array. */ @@ -46,7 +46,16 @@ exports.defaultConfig = [ ".git/**" ], languageOptions: { - parser: "@/espree" + ecmaVersion: "latest", + sourceType: "module", + parser: "@/espree", + parserOptions: {} + } + }, + { + files: ["**/*.cjs"], + languageOptions: { + sourceType: "commonjs" } } ]; diff --git a/eslint/lib/config/flat-config-array.js b/eslint/lib/config/flat-config-array.js index ef9cb33..c06fd92 100644 --- a/eslint/lib/config/flat-config-array.js +++ b/eslint/lib/config/flat-config-array.js @@ -52,13 +52,13 @@ class FlatConfigArray extends ConfigArray { * @param {{basePath: string, baseConfig: FlatConfig}} options The options * to use for the config array instance. */ - constructor(configs, { basePath, baseConfig = defaultConfig }) { + constructor(configs, { basePath, baseConfig = defaultConfig } = {}) { super(configs, { basePath, schema: flatConfigSchema }); - this.unshift(baseConfig); + this.unshift(...baseConfig); } /* eslint-disable class-methods-use-this -- Desired as instance method */ diff --git a/eslint/lib/config/flat-config-helpers.js b/eslint/lib/config/flat-config-helpers.js new file mode 100644 index 0000000..778f129 --- /dev/null +++ b/eslint/lib/config/flat-config-helpers.js @@ -0,0 +1,67 @@ +/** + * @fileoverview Shared functions to work with configs. + * @author Nicholas C. Zakas + */ + +"use strict"; + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +/** + * Parses a ruleId into its plugin and rule parts. + * @param {string} ruleId The rule ID to parse. + * @returns {{pluginName:string,ruleName:string}} The plugin and rule + * parts of the ruleId; + */ +function parseRuleId(ruleId) { + let pluginName, ruleName; + + // distinguish between core rules and plugin rules + if (ruleId.includes("/")) { + pluginName = ruleId.slice(0, ruleId.lastIndexOf("/")); + ruleName = ruleId.slice(pluginName.length + 1); + } else { + pluginName = "@"; + ruleName = ruleId; + } + + return { + pluginName, + ruleName + }; +} + +/** + * Retrieves a rule instance from a given config based on the ruleId. + * @param {string} ruleId The rule ID to look for. + * @param {FlatConfig} config The config to search. + * @returns {import("../shared/types").Rule|undefined} The rule if found + * or undefined if not. + */ +function getRuleFromConfig(ruleId, config) { + + const { pluginName, ruleName } = parseRuleId(ruleId); + + const plugin = config.plugins && config.plugins[pluginName]; + let rule = plugin && plugin.rules && plugin.rules[ruleName]; + + // normalize function rules into objects + if (rule && typeof rule === "function") { + rule = { + create: rule + }; + } + + return rule; +} + +//----------------------------------------------------------------------------- +// Exports +//----------------------------------------------------------------------------- + +module.exports = { + parseRuleId, + getRuleFromConfig +}; diff --git a/eslint/lib/config/flat-config-schema.js b/eslint/lib/config/flat-config-schema.js index c8cc711..cb8e796 100644 --- a/eslint/lib/config/flat-config-schema.js +++ b/eslint/lib/config/flat-config-schema.js @@ -195,13 +195,6 @@ function assertIsObjectOrString(value) { // Low-Level Schemas //----------------------------------------------------------------------------- - -/** @type {ObjectPropertySchema} */ -const numberSchema = { - merge: "replace", - validate: "number" -}; - /** @type {ObjectPropertySchema} */ const booleanSchema = { merge: "replace", @@ -415,6 +408,18 @@ const rulesSchema = { } }; +/** @type {ObjectPropertySchema} */ +const ecmaVersionSchema = { + merge: "replace", + validate(value) { + if (typeof value === "number" || value === "latest") { + return; + } + + throw new TypeError("Expected a number or \"latest\"."); + } +}; + /** @type {ObjectPropertySchema} */ const sourceTypeSchema = { merge: "replace", @@ -439,7 +444,7 @@ exports.flatConfigSchema = { }, languageOptions: { schema: { - ecmaVersion: numberSchema, + ecmaVersion: ecmaVersionSchema, sourceType: sourceTypeSchema, globals: globalsSchema, parser: parserSchema, diff --git a/eslint/lib/config/rule-validator.js b/eslint/lib/config/rule-validator.js index 527a56e..9172f93 100644 --- a/eslint/lib/config/rule-validator.js +++ b/eslint/lib/config/rule-validator.js @@ -10,52 +10,49 @@ //----------------------------------------------------------------------------- const ajv = require("../shared/ajv")(); +const { parseRuleId, getRuleFromConfig } = require("./flat-config-helpers"); +const ruleReplacements = require("../../conf/replacements.json"); //----------------------------------------------------------------------------- // Helpers //----------------------------------------------------------------------------- /** - * Finds a rule with the given ID in the given config. - * @param {string} ruleId The ID of the rule to find. + * Throws a helpful error when a rule cannot be found. + * @param {Object} ruleId The rule identifier. + * @param {string} ruleId.pluginName The ID of the rule to find. + * @param {string} ruleId.ruleName The ID of the rule to find. * @param {Object} config The config to search in. * @throws {TypeError} For missing plugin or rule. - * @returns {{create: Function, schema: (Array|null)}} THe rule object. + * @returns {void} */ -function findRuleDefinition(ruleId, config) { - const ruleIdParts = ruleId.split("/"); - let pluginName, ruleName; - - // built-in rule - if (ruleIdParts.length === 1) { - pluginName = "@"; - ruleName = ruleIdParts[0]; - } else { - ruleName = ruleIdParts.pop(); - pluginName = ruleIdParts.join("/"); - } +function throwRuleNotFoundError({ pluginName, ruleName }, config) { + + const ruleId = pluginName === "@" ? ruleName : `${pluginName}/${ruleName}`; const errorMessageHeader = `Key "rules": Key "${ruleId}"`; let errorMessage = `${errorMessageHeader}: Could not find plugin "${pluginName}".`; // if the plugin exists then we need to check if the rule exists if (config.plugins && config.plugins[pluginName]) { + const replacementRuleName = ruleReplacements.rules[ruleName]; - const plugin = config.plugins[pluginName]; + if (pluginName === "@" && replacementRuleName) { - // first check for exact rule match - if (plugin.rules && plugin.rules[ruleName]) { - return config.plugins[pluginName].rules[ruleName]; - } + errorMessage = `${errorMessageHeader}: Rule "${ruleName}" was removed and replaced by "${replacementRuleName}".`; + + } else { - errorMessage = `${errorMessageHeader}: Could not find "${ruleName}" in plugin "${pluginName}".`; + errorMessage = `${errorMessageHeader}: Could not find "${ruleName}" in plugin "${pluginName}".`; - // otherwise, let's see if we can find the rule name elsewhere - for (const [otherPluginName, otherPlugin] of Object.entries(config.plugins)) { - if (otherPlugin.rules && otherPlugin.rules[ruleName]) { - errorMessage += ` Did you mean "${otherPluginName}/${ruleName}"?`; - break; + // otherwise, let's see if we can find the rule name elsewhere + for (const [otherPluginName, otherPlugin] of Object.entries(config.plugins)) { + if (otherPlugin.rules && otherPlugin.rules[ruleName]) { + errorMessage += ` Did you mean "${otherPluginName}/${ruleName}"?`; + break; + } } + } // falls through to throw error @@ -154,7 +151,11 @@ class RuleValidator { continue; } - const rule = findRuleDefinition(ruleId, config); + const rule = getRuleFromConfig(ruleId, config); + + if (!rule) { + throwRuleNotFoundError(parseRuleId(ruleId), config); + } // Precompile and cache validator the first time if (!this.validators.has(rule)) { diff --git a/eslint/lib/eslint/eslint.js b/eslint/lib/eslint/eslint.js index 8886d45..6274772 100644 --- a/eslint/lib/eslint/eslint.js +++ b/eslint/lib/eslint/eslint.js @@ -34,7 +34,12 @@ const { version } = require("../../package.json"); /** @typedef {import("../shared/types").LintMessage} LintMessage */ /** @typedef {import("../shared/types").Plugin} Plugin */ /** @typedef {import("../shared/types").Rule} Rule */ -/** @typedef {import("./load-formatter").Formatter} Formatter */ + +/** + * The main formatter object. + * @typedef Formatter + * @property {function(LintResult[]): string | Promise} format format function. + */ /** * The options with which to configure the ESLint instance. @@ -617,7 +622,7 @@ class ESLint { throw new Error("'name' must be a string"); } - const { cliEngine } = privateMembersMap.get(this); + const { cliEngine, options } = privateMembersMap.get(this); const formatter = cliEngine.getFormatter(name); if (typeof formatter !== "function") { @@ -629,7 +634,7 @@ class ESLint { /** * The main formatter method. * @param {LintResults[]} results The lint results to format. - * @returns {string} The formatted lint results. + * @returns {string | Promise} The formatted lint results. */ format(results) { let rulesMeta = null; @@ -637,6 +642,9 @@ class ESLint { results.sort(compareResultsByFilePath); return formatter(results, { + get cwd() { + return options.cwd; + }, get rulesMeta() { if (!rulesMeta) { rulesMeta = createRulesMeta(cliEngine.getRules()); diff --git a/eslint/lib/linter/linter.js b/eslint/lib/linter/linter.js index 4e07a25..f897b8d 100644 --- a/eslint/lib/linter/linter.js +++ b/eslint/lib/linter/linter.js @@ -37,6 +37,8 @@ const SourceCodeFixer = require("./source-code-fixer"), timing = require("./timing"), ruleReplacements = require("../../conf/replacements.json"); +const { getRuleFromConfig } = require("../config/flat-config-helpers"); +const { FlatConfigArray } = require("../config/flat-config-array"); const debug = require("debug")("eslint:linter"); const MAX_AUTOFIX_PASSES = 10; @@ -45,6 +47,7 @@ const DEFAULT_ECMA_VERSION = 5; const commentParser = new ConfigCommentParser(); const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } }; const parserSymbol = Symbol.for("eslint.RuleTester.parser"); +const globals = require("../../conf/globals"); //------------------------------------------------------------------------------ // Typedefs @@ -57,6 +60,7 @@ const parserSymbol = Symbol.for("eslint.RuleTester.parser"); /** @typedef {import("../shared/types").GlobalConf} GlobalConf */ /** @typedef {import("../shared/types").LintMessage} LintMessage */ /** @typedef {import("../shared/types").ParserOptions} ParserOptions */ +/** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */ /** @typedef {import("../shared/types").Processor} Processor */ /** @typedef {import("../shared/types").Rule} Rule */ @@ -126,6 +130,38 @@ const parserSymbol = Symbol.for("eslint.RuleTester.parser"); // Helpers //------------------------------------------------------------------------------ +/** + * Determines if a given object is Espree. + * @param {Object} parser The parser to check. + * @returns {boolean} True if the parser is Espree or false if not. + */ +function isEspree(parser) { + return !!(parser === espree || parser[parserSymbol] === espree); +} + +/** + * Retrieves globals for the given ecmaVersion. + * @param {number} ecmaVersion The version to retrieve globals for. + * @returns {Object} The globals for the given ecmaVersion. + */ +function getGlobalsForEcmaVersion(ecmaVersion) { + + switch (ecmaVersion) { + case 3: + return globals.es3; + + case 5: + return globals.es5; + + default: + if (ecmaVersion < 2015) { + return globals[`es${ecmaVersion + 2009}`]; + } + + return globals[`es${ecmaVersion}`]; + } +} + /** * Ensures that variables representing built-in properties of the Global Object, * and any globals declared by special block comments, are present in the global @@ -268,7 +304,7 @@ function createDisableDirectives(options) { for (const ruleId of directiveRules) { // push to directives, if the rule is defined(including null, e.g. /*eslint enable*/) - if (ruleId === null || ruleMapper(ruleId) !== null) { + if (ruleId === null || !!ruleMapper(ruleId)) { result.directives.push({ parentComment, type, line: commentToken.loc.start.line, column: commentToken.loc.start.column + 1, ruleId }); } else { result.directiveProblems.push(createLintingProblem({ ruleId, loc: commentToken.loc })); @@ -400,7 +436,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) { const rule = ruleMapper(name); const ruleValue = parseResult.config[name]; - if (rule === null) { + if (!rule) { problems.push(createLintingProblem({ ruleId: name, loc: comment.loc })); return; } @@ -447,7 +483,8 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) { * @returns {number} normalized ECMAScript version */ function normalizeEcmaVersion(parser, ecmaVersion) { - if ((parser[parserSymbol] || parser) === espree) { + + if (isEspree(parser)) { if (ecmaVersion === "latest") { return espree.latestEcmaVersion; } @@ -460,6 +497,38 @@ function normalizeEcmaVersion(parser, ecmaVersion) { return ecmaVersion >= 2015 ? ecmaVersion - 2009 : ecmaVersion; } +/** + * Normalize ECMAScript version from the initial config into languageOptions (year) + * format. + * @param {any} [ecmaVersion] ECMAScript version from the initial config + * @returns {number} normalized ECMAScript version + */ +function normalizeEcmaVersionForLanguageOptions(ecmaVersion) { + + switch (ecmaVersion) { + case 3: + return 3; + + // void 0 = no ecmaVersion specified so use the default + case 5: + case void 0: + return 5; + + default: + if (typeof ecmaVersion === "number") { + return ecmaVersion >= 2015 ? ecmaVersion : ecmaVersion + 2009; + } + } + + /* + * We default to the latest supported ecmaVersion for everything else. + * Remember, this is for languageOptions.ecmaVersion, which sets the version + * that is used for a number of processes inside of ESLint. It's normally + * safe to assume people want the latest unless otherwise specified. + */ + return espree.latestEcmaVersion + 2009; +} + const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)(?:\*\/|$)/gsu; /** @@ -511,7 +580,11 @@ function normalizeFilename(filename) { * @returns {Required & InternalOptions} Normalized options */ function normalizeVerifyOptions(providedOptions, config) { - const disableInlineConfig = config.noInlineConfig === true; + + const linterOptions = config.linterOptions || config; + + // .noInlineConfig for eslintrc, .linterOptions.noInlineConfig for flat + const disableInlineConfig = linterOptions.noInlineConfig === true; const ignoreInlineConfig = providedOptions.allowInlineConfig === false; const configNameOfNoInlineConfig = config.configNameOfNoInlineConfig ? ` (${config.configNameOfNoInlineConfig})` @@ -523,7 +596,9 @@ function normalizeVerifyOptions(providedOptions, config) { reportUnusedDisableDirectives = reportUnusedDisableDirectives ? "error" : "off"; } if (typeof reportUnusedDisableDirectives !== "string") { - reportUnusedDisableDirectives = config.reportUnusedDisableDirectives ? "warn" : "off"; + reportUnusedDisableDirectives = + linterOptions.reportUnusedDisableDirectives + ? "warn" : "off"; } return { @@ -566,6 +641,30 @@ function resolveParserOptions(parser, providedOptions, enabledEnvironments) { return mergedParserOptions; } +/** + * Converts parserOptions to languageOptions for backwards compatibility with eslintrc. + * @param {ConfigData} config Config object. + * @param {Object} config.globals Global variable definitions. + * @param {Parser} config.parser The parser to use. + * @param {ParserOptions} config.parserOptions The parserOptions to use. + * @returns {LanguageOptions} The languageOptions equivalent. + */ +function createLanguageOptions({ globals: configuredGlobals, parser, parserOptions }) { + + const { + ecmaVersion, + sourceType + } = parserOptions; + + return { + globals: configuredGlobals, + ecmaVersion: normalizeEcmaVersionForLanguageOptions(ecmaVersion), + sourceType, + parser, + parserOptions + }; +} + /** * Combines the provided globals object with the globals from environments * @param {Record} providedGlobals The 'globals' key in a config @@ -614,20 +713,21 @@ function getRuleOptions(ruleConfig) { /** * Analyze scope of the given AST. * @param {ASTNode} ast The `Program` node to analyze. - * @param {ParserOptions} parserOptions The parser options. + * @param {LanguageOptions} languageOptions The parser options. * @param {Record} visitorKeys The visitor keys. * @returns {ScopeManager} The analysis result. */ -function analyzeScope(ast, parserOptions, visitorKeys) { +function analyzeScope(ast, languageOptions, visitorKeys) { + const parserOptions = languageOptions.parserOptions; const ecmaFeatures = parserOptions.ecmaFeatures || {}; - const ecmaVersion = parserOptions.ecmaVersion || DEFAULT_ECMA_VERSION; + const ecmaVersion = languageOptions.ecmaVersion || DEFAULT_ECMA_VERSION; return eslintScope.analyze(ast, { ignoreEval: true, nodejsScope: ecmaFeatures.globalReturn, impliedStrict: ecmaFeatures.impliedStrict, ecmaVersion: typeof ecmaVersion === "number" ? ecmaVersion : 6, - sourceType: parserOptions.sourceType || "script", + sourceType: languageOptions.sourceType || "script", childVisitorKeys: visitorKeys || evk.KEYS, fallback: Traverser.getKeys }); @@ -638,25 +738,29 @@ function analyzeScope(ast, parserOptions, visitorKeys) { * optimization of functions, so it's best to keep the try-catch as isolated * as possible * @param {string} text The text to parse. - * @param {Parser} parser The parser to parse. - * @param {ParserOptions} providedParserOptions Options to pass to the parser + * @param {LanguageOptions} languageOptions Options to pass to the parser * @param {string} filePath The path to the file being parsed. * @returns {{success: false, error: Problem}|{success: true, sourceCode: SourceCode}} * An object containing the AST and parser services if parsing was successful, or the error if parsing failed * @private */ -function parse(text, parser, providedParserOptions, filePath) { +function parse(text, languageOptions, filePath) { const textToParse = stripUnicodeBOM(text).replace(astUtils.shebangPattern, (match, captured) => `//${captured}`); - const parserOptions = Object.assign({}, providedParserOptions, { - loc: true, - range: true, - raw: true, - tokens: true, - comment: true, - eslintVisitorKeys: true, - eslintScopeManager: true, - filePath - }); + const { ecmaVersion, sourceType, parser } = languageOptions; + const parserOptions = Object.assign( + { ecmaVersion, sourceType }, + languageOptions.parserOptions, + { + loc: true, + range: true, + raw: true, + tokens: true, + comment: true, + eslintVisitorKeys: true, + eslintScopeManager: true, + filePath + } + ); /* * Check for parsing errors first. If there's a parsing error, nothing @@ -671,7 +775,7 @@ function parse(text, parser, providedParserOptions, filePath) { const ast = parseResult.ast; const parserServices = parseResult.services || {}; const visitorKeys = parseResult.visitorKeys || evk.KEYS; - const scopeManager = parseResult.scopeManager || analyzeScope(ast, parserOptions, visitorKeys); + const scopeManager = parseResult.scopeManager || analyzeScope(ast, languageOptions, visitorKeys); return { success: true, @@ -740,13 +844,17 @@ function getScope(scopeManager, currentNode) { * Marks a variable as used in the current scope * @param {ScopeManager} scopeManager The scope manager for this AST. The scope may be mutated by this function. * @param {ASTNode} currentNode The node currently being traversed - * @param {Object} parserOptions The options used to parse this text + * @param {LanguageOptions} languageOptions The options used to parse this text * @param {string} name The name of the variable that should be marked as used. * @returns {boolean} True if the variable was found and marked as used, false if not. */ -function markVariableAsUsed(scopeManager, currentNode, parserOptions, name) { - const hasGlobalReturn = parserOptions.ecmaFeatures && parserOptions.ecmaFeatures.globalReturn; - const specialScope = hasGlobalReturn || parserOptions.sourceType === "module"; +function markVariableAsUsed(scopeManager, currentNode, languageOptions, name) { + const parserOptions = languageOptions.parserOptions; + const sourceType = languageOptions.sourceType; + const hasGlobalReturn = + (parserOptions.ecmaFeatures && parserOptions.ecmaFeatures.globalReturn) || + sourceType === "commonjs"; + const specialScope = hasGlobalReturn || sourceType === "module"; const currentScope = getScope(scopeManager, currentNode); // Special Node.js scope means we need to start one level deeper @@ -837,8 +945,8 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze( * @param {SourceCode} sourceCode A SourceCode object for the given text * @param {Object} configuredRules The rules configuration * @param {function(string): Rule} ruleMapper A mapper function from rule names to rules - * @param {Object} parserOptions The options that were passed to the parser - * @param {string} parserName The name of the parser in the config + * @param {string | undefined} parserName The name of the parser in the config + * @param {LanguageOptions} languageOptions The options for parsing the code. * @param {Object} settings The settings that were enabled in the config * @param {string} filename The reported filename of the code * @param {boolean} disableFixes If true, it doesn't make `fix` properties. @@ -846,7 +954,7 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze( * @param {string} physicalFilename The full path of the file on disk without any code block information * @returns {Problem[]} An array of reported problems */ -function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes, cwd, physicalFilename) { +function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageOptions, settings, filename, disableFixes, cwd, physicalFilename) { const emitter = createEmitter(); const nodeQueue = []; let currentNode = sourceCode.ast; @@ -878,16 +986,18 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser getPhysicalFilename: () => physicalFilename || filename, getScope: () => getScope(sourceCode.scopeManager, currentNode), getSourceCode: () => sourceCode, - markVariableAsUsed: name => markVariableAsUsed(sourceCode.scopeManager, currentNode, parserOptions, name), - parserOptions, + markVariableAsUsed: name => markVariableAsUsed(sourceCode.scopeManager, currentNode, languageOptions, name), + parserOptions: { + ...languageOptions.parserOptions + }, parserPath: parserName, + languageOptions, parserServices: sourceCode.parserServices, settings } ) ); - const lintingProblems = []; Object.keys(configuredRules).forEach(ruleId => { @@ -900,7 +1010,7 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser const rule = ruleMapper(ruleId); - if (rule === null) { + if (!rule) { lintingProblems.push(createLintingProblem({ ruleId })); return; } @@ -1074,13 +1184,28 @@ function normalizeCwd(cwd) { */ const internalSlotsMap = new WeakMap(); +/** + * Throws an error when the given linter is in flat config mode. + * @param {Linter} linter The linter to check. + * @returns {void} + * @throws {Error} If the linter is in flat config mode. + */ +function assertEslintrcConfig(linter) { + const { configType } = internalSlotsMap.get(linter); + + if (configType === "flat") { + throw new Error("This method cannot be used with flat config. Add your entries directly into the config array."); + } +} + + //------------------------------------------------------------------------------ // Public Interface //------------------------------------------------------------------------------ /** * Object that is responsible for verifying JavaScript text - * @name eslint + * @name Linter */ class Linter { @@ -1088,12 +1213,14 @@ class Linter { * Initialize the Linter. * @param {Object} [config] the config object * @param {string} [config.cwd] path to a directory that should be considered as the current working directory, can be undefined. + * @param {"flat"|"eslintrc"} [config.configType="eslintrc"] the type of config used. */ - constructor({ cwd } = {}) { + constructor({ cwd, configType } = {}) { internalSlotsMap.set(this, { cwd: normalizeCwd(cwd), lastConfigArray: null, lastSourceCode: null, + configType, // TODO: Remove after flat config conversion parserMap: new Map([["espree", espree]]), ruleMap: new Rules() }); @@ -1168,12 +1295,16 @@ class Linter { const parserOptions = resolveParserOptions(parser, config.parserOptions || {}, enabledEnvs); const configuredGlobals = resolveGlobals(config.globals || {}, enabledEnvs); const settings = config.settings || {}; + const languageOptions = createLanguageOptions({ + globals: config.globals, + parser, + parserOptions + }); if (!slots.lastSourceCode) { const parseResult = parse( text, - parser, - parserOptions, + languageOptions, options.filename ); @@ -1194,7 +1325,7 @@ class Linter { ast: slots.lastSourceCode.ast, parserServices: slots.lastSourceCode.parserServices, visitorKeys: slots.lastSourceCode.visitorKeys, - scopeManager: analyzeScope(slots.lastSourceCode.ast, parserOptions) + scopeManager: analyzeScope(slots.lastSourceCode.ast, languageOptions) }); } } @@ -1220,8 +1351,8 @@ class Linter { sourceCode, configuredRules, ruleId => getRule(slots, ruleId), - parserOptions, parserName, + languageOptions, settings, options.filename, options.disableFixes, @@ -1270,15 +1401,43 @@ class Linter { */ verify(textOrSourceCode, config, filenameOrOptions) { debug("Verify"); + + const { configType } = internalSlotsMap.get(this); + const options = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions } : filenameOrOptions || {}; - // CLIEngine passes a `ConfigArray` object. - if (config && typeof config.extractConfig === "function") { - return this._verifyWithConfigArray(textOrSourceCode, config, options); + if (config) { + if (configType === "flat") { + + /* + * Because of how Webpack packages up the files, we can't + * compare directly to `FlatConfigArray` using `instanceof` + * because it's not the same `FlatConfigArray` as in the tests. + * So, we work around it by assuming an array is, in fact, a + * `FlatConfigArray` if it has a `getConfig()` method. + */ + let configArray = config; + + if (!Array.isArray(config) || typeof config.getConfig !== "function") { + configArray = new FlatConfigArray(config); + configArray.normalizeSync(); + } + + return this._verifyWithFlatConfigArray(textOrSourceCode, configArray, options, true); + } + + if (typeof config.extractConfig === "function") { + return this._verifyWithConfigArray(textOrSourceCode, config, options); + } } + /* + * If we get to here, it means `config` is just an object rather + * than a config array so we can go right into linting. + */ + /* * `Linter` doesn't support `overrides` property in configuration. * So we cannot apply multiple processors. @@ -1289,6 +1448,214 @@ class Linter { return this._verifyWithoutProcessors(textOrSourceCode, config, options); } + /** + * Verify with a processor. + * @param {string|SourceCode} textOrSourceCode The source code. + * @param {FlatConfig} config The config array. + * @param {VerifyOptions&ProcessorOptions} options The options. + * @param {FlatConfigArray} [configForRecursive] The `ConfigArray` object to apply multiple processors recursively. + * @returns {LintMessage[]} The found problems. + */ + _verifyWithFlatConfigArrayAndProcessor(textOrSourceCode, config, options, configForRecursive) { + const filename = options.filename || ""; + const filenameToExpose = normalizeFilename(filename); + const physicalFilename = options.physicalFilename || filenameToExpose; + const text = ensureText(textOrSourceCode); + const preprocess = options.preprocess || (rawText => [rawText]); + const postprocess = options.postprocess || (messagesList => messagesList.flat()); + const filterCodeBlock = + options.filterCodeBlock || + (blockFilename => blockFilename.endsWith(".js")); + const originalExtname = path.extname(filename); + const messageLists = preprocess(text, filenameToExpose).map((block, i) => { + debug("A code block was found: %o", block.filename || "(unnamed)"); + + // Keep the legacy behavior. + if (typeof block === "string") { + return this._verifyWithFlatConfigArrayAndWithoutProcessors(block, config, options); + } + + const blockText = block.text; + const blockName = path.join(filename, `${i}_${block.filename}`); + + // Skip this block if filtered. + if (!filterCodeBlock(blockName, blockText)) { + debug("This code block was skipped."); + return []; + } + + // Resolve configuration again if the file content or extension was changed. + if (configForRecursive && (text !== blockText || path.extname(blockName) !== originalExtname)) { + debug("Resolving configuration again because the file content or extension was changed."); + return this._verifyWithFlatConfigArray( + blockText, + configForRecursive, + { ...options, filename: blockName, physicalFilename } + ); + } + + // Does lint. + return this._verifyWithFlatConfigArrayAndWithoutProcessors( + blockText, + config, + { ...options, filename: blockName, physicalFilename } + ); + }); + + return postprocess(messageLists, filenameToExpose); + } + + /** + * Same as linter.verify, except without support for processors. + * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object. + * @param {FlatConfig} providedConfig An ESLintConfig instance to configure everything. + * @param {VerifyOptions} [providedOptions] The optional filename of the file being checked. + * @throws {Error} If during rule execution. + * @returns {LintMessage[]} The results as an array of messages or an empty array if no messages. + */ + _verifyWithFlatConfigArrayAndWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) { + const slots = internalSlotsMap.get(this); + const config = providedConfig || {}; + const options = normalizeVerifyOptions(providedOptions, config); + let text; + + // evaluate arguments + if (typeof textOrSourceCode === "string") { + slots.lastSourceCode = null; + text = textOrSourceCode; + } else { + slots.lastSourceCode = textOrSourceCode; + text = textOrSourceCode.text; + } + + const languageOptions = config.languageOptions; + + languageOptions.ecmaVersion = normalizeEcmaVersionForLanguageOptions( + languageOptions.ecmaVersion + ); + + // add configured globals and language globals + const configuredGlobals = { + ...(getGlobalsForEcmaVersion(languageOptions.ecmaVersion)), + ...(languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0), + ...languageOptions.globals + }; + + // Espree expects this information to be passed in + if (isEspree(languageOptions.parser)) { + const parserOptions = languageOptions.parserOptions; + + if (languageOptions.sourceType) { + + parserOptions.sourceType = languageOptions.sourceType; + + if ( + parserOptions.sourceType === "module" && + parserOptions.ecmaFeatures && + parserOptions.ecmaFeatures.globalReturn + ) { + parserOptions.ecmaFeatures.globalReturn = false; + } + } + } + + const settings = config.settings || {}; + + if (!slots.lastSourceCode) { + const parseResult = parse( + text, + languageOptions, + options.filename + ); + + if (!parseResult.success) { + return [parseResult.error]; + } + + slots.lastSourceCode = parseResult.sourceCode; + } else { + + /* + * If the given source code object as the first argument does not have scopeManager, analyze the scope. + * This is for backward compatibility (SourceCode is frozen so it cannot rebind). + */ + if (!slots.lastSourceCode.scopeManager) { + slots.lastSourceCode = new SourceCode({ + text: slots.lastSourceCode.text, + ast: slots.lastSourceCode.ast, + parserServices: slots.lastSourceCode.parserServices, + visitorKeys: slots.lastSourceCode.visitorKeys, + scopeManager: analyzeScope(slots.lastSourceCode.ast, languageOptions) + }); + } + } + + const sourceCode = slots.lastSourceCode; + const commentDirectives = options.allowInlineConfig + ? getDirectiveComments( + options.filename, + sourceCode.ast, + ruleId => getRuleFromConfig(ruleId, config), + options.warnInlineConfig + ) + : { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] }; + + // augment global scope with declared global variables + addDeclaredGlobals( + sourceCode.scopeManager.scopes[0], + configuredGlobals, + { exportedVariables: commentDirectives.exportedVariables, enabledGlobals: commentDirectives.enabledGlobals } + ); + + const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules); + + let lintingProblems; + + try { + lintingProblems = runRules( + sourceCode, + configuredRules, + ruleId => getRuleFromConfig(ruleId, config), + void 0, + languageOptions, + settings, + options.filename, + options.disableFixes, + slots.cwd, + providedOptions.physicalFilename + ); + } catch (err) { + err.message += `\nOccurred while linting ${options.filename}`; + debug("An error occurred while traversing"); + debug("Filename:", options.filename); + if (err.currentNode) { + const { line } = err.currentNode.loc.start; + + debug("Line:", line); + err.message += `:${line}`; + } + debug("Parser Options:", languageOptions.parserOptions); + + // debug("Parser Path:", parserName); + debug("Settings:", settings); + + if (err.ruleId) { + err.message += `\nRule: "${err.ruleId}"`; + } + + throw err; + } + + return applyDisableDirectives({ + directives: commentDirectives.disableDirectives, + disableFixes: options.disableFixes, + problems: lintingProblems + .concat(commentDirectives.problems) + .sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column), + reportUnusedDisableDirectives: options.reportUnusedDisableDirectives + }); + } + /** * Verify a given code with `ConfigArray`. * @param {string|SourceCode} textOrSourceCode The source code. @@ -1324,6 +1691,47 @@ class Linter { return this._verifyWithoutProcessors(textOrSourceCode, config, options); } + /** + * Verify a given code with a flat config. + * @param {string|SourceCode} textOrSourceCode The source code. + * @param {FlatConfigArray} configArray The config array. + * @param {VerifyOptions&ProcessorOptions} options The options. + * @param {boolean} [firstCall=false] Indicates if this is being called directly + * from verify(). (TODO: Remove once eslintrc is removed.) + * @returns {LintMessage[]} The found problems. + */ + _verifyWithFlatConfigArray(textOrSourceCode, configArray, options, firstCall = false) { + debug("With flat config: %s", options.filename); + + // we need a filename to match configs against + const filename = options.filename || ""; + + // Store the config array in order to get plugin envs and rules later. + internalSlotsMap.get(this).lastConfigArray = configArray; + const config = configArray.getConfig(filename); + + // Verify. + if (config.processor) { + debug("Apply the processor: %o", config.processor); + const { preprocess, postprocess, supportsAutofix } = config.processor; + const disableFixes = options.disableFixes || !supportsAutofix; + + return this._verifyWithFlatConfigArrayAndProcessor( + textOrSourceCode, + config, + { ...options, filename, disableFixes, postprocess, preprocess }, + configArray + ); + } + + // check for options-based processing + if (firstCall && (options.preprocess || options.postprocess)) { + return this._verifyWithFlatConfigArrayAndProcessor(textOrSourceCode, config, options); + } + + return this._verifyWithFlatConfigArrayAndWithoutProcessors(textOrSourceCode, config, options); + } + /** * Verify with a processor. * @param {string|SourceCode} textOrSourceCode The source code. @@ -1397,6 +1805,7 @@ class Linter { * @returns {void} */ defineRule(ruleId, ruleModule) { + assertEslintrcConfig(this); internalSlotsMap.get(this).ruleMap.define(ruleId, ruleModule); } @@ -1406,6 +1815,7 @@ class Linter { * @returns {void} */ defineRules(rulesToDefine) { + assertEslintrcConfig(this); Object.getOwnPropertyNames(rulesToDefine).forEach(ruleId => { this.defineRule(ruleId, rulesToDefine[ruleId]); }); @@ -1416,6 +1826,7 @@ class Linter { * @returns {Map} All loaded rules */ getRules() { + assertEslintrcConfig(this); const { lastConfigArray, ruleMap } = internalSlotsMap.get(this); return new Map(function *() { @@ -1434,6 +1845,7 @@ class Linter { * @returns {void} */ defineParser(parserId, parserModule) { + assertEslintrcConfig(this); internalSlotsMap.get(this).parserMap.set(parserId, parserModule); } @@ -1441,7 +1853,7 @@ class Linter { * Performs multiple autofix passes over the text until as many fixes as possible * have been applied. * @param {string} text The source text to apply fixes to. - * @param {ConfigData|ConfigArray} config The ESLint config object to use. + * @param {ConfigData|ConfigArray|FlatConfigArray} config The ESLint config object to use. * @param {VerifyOptions&ProcessorOptions&FixOptions} options The ESLint options object to use. * @returns {{fixed:boolean,messages:LintMessage[],output:string}} The result of the fix operation as returned from the * SourceCodeFixer. diff --git a/eslint/lib/options.js b/eslint/lib/options.js index 2dd186d..6d06e3d 100644 --- a/eslint/lib/options.js +++ b/eslint/lib/options.js @@ -50,7 +50,7 @@ const optionator = require("optionator"); * @property {boolean | undefined} reportUnusedDisableDirectives Adds reported errors for unused eslint-disable directives * @property {string} [resolvePluginsRelativeTo] A folder where plugins should be resolved from, CWD by default * @property {Object} [rule] Specify rules - * @property {string[]} [rulesdir] Use additional rules from this directory + * @property {string[]} [rulesdir] Load additional rules from this directory. Deprecated: Use rules from plugins * @property {boolean} stdin Lint code provided on * @property {string} [stdinFilename] Specify filename to process STDIN as * @property {boolean} quiet Report errors only @@ -118,11 +118,6 @@ module.exports = optionator({ { heading: "Specifying rules and plugins" }, - { - option: "rulesdir", - type: "[path::String]", - description: "Use additional rules from this directory" - }, { option: "plugin", type: "[String]", @@ -133,6 +128,11 @@ module.exports = optionator({ type: "Object", description: "Specify rules" }, + { + option: "rulesdir", + type: "[path::String]", + description: "Load additional rules from this directory. Deprecated: Use rules from plugins" + }, { heading: "Fixing problems" }, diff --git a/eslint/lib/rules/accessor-pairs.js b/eslint/lib/rules/accessor-pairs.js index f047252..2fcf153 100644 --- a/eslint/lib/rules/accessor-pairs.js +++ b/eslint/lib/rules/accessor-pairs.js @@ -134,6 +134,7 @@ function isPropertyDescriptor(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/array-bracket-newline.js b/eslint/lib/rules/array-bracket-newline.js index 28a05b3..0beb138 100644 --- a/eslint/lib/rules/array-bracket-newline.js +++ b/eslint/lib/rules/array-bracket-newline.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/array-bracket-spacing.js b/eslint/lib/rules/array-bracket-spacing.js index 1eea99c..e4912ec 100644 --- a/eslint/lib/rules/array-bracket-spacing.js +++ b/eslint/lib/rules/array-bracket-spacing.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/array-callback-return.js b/eslint/lib/rules/array-callback-return.js index d13ecd7..fba414c 100644 --- a/eslint/lib/rules/array-callback-return.js +++ b/eslint/lib/rules/array-callback-return.js @@ -133,6 +133,7 @@ function getArrayMethodName(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/array-element-newline.js b/eslint/lib/rules/array-element-newline.js index 535fa21..77f5fc9 100644 --- a/eslint/lib/rules/array-element-newline.js +++ b/eslint/lib/rules/array-element-newline.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/arrow-body-style.js b/eslint/lib/rules/arrow-body-style.js index 3a3f544..7a141b0 100644 --- a/eslint/lib/rules/arrow-body-style.js +++ b/eslint/lib/rules/arrow-body-style.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/arrow-parens.js b/eslint/lib/rules/arrow-parens.js index 4f4dea0..779ab6f 100644 --- a/eslint/lib/rules/arrow-parens.js +++ b/eslint/lib/rules/arrow-parens.js @@ -27,6 +27,7 @@ function hasBlockBody(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/arrow-spacing.js b/eslint/lib/rules/arrow-spacing.js index 9e1ed71..9fdcdd5 100644 --- a/eslint/lib/rules/arrow-spacing.js +++ b/eslint/lib/rules/arrow-spacing.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/block-scoped-var.js b/eslint/lib/rules/block-scoped-var.js index d98250b..3a27786 100644 --- a/eslint/lib/rules/block-scoped-var.js +++ b/eslint/lib/rules/block-scoped-var.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/block-spacing.js b/eslint/lib/rules/block-spacing.js index 990b441..53303a9 100644 --- a/eslint/lib/rules/block-spacing.js +++ b/eslint/lib/rules/block-spacing.js @@ -11,6 +11,7 @@ const util = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/brace-style.js b/eslint/lib/rules/brace-style.js index 89f9ba5..f4adb94 100644 --- a/eslint/lib/rules/brace-style.js +++ b/eslint/lib/rules/brace-style.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/callback-return.js b/eslint/lib/rules/callback-return.js index 449b9a9..532faee 100644 --- a/eslint/lib/rules/callback-return.js +++ b/eslint/lib/rules/callback-return.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/camelcase.js b/eslint/lib/rules/camelcase.js index 7e8fc68..61dd062 100644 --- a/eslint/lib/rules/camelcase.js +++ b/eslint/lib/rules/camelcase.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/capitalized-comments.js b/eslint/lib/rules/capitalized-comments.js index e5f4293..6ea87a7 100644 --- a/eslint/lib/rules/capitalized-comments.js +++ b/eslint/lib/rules/capitalized-comments.js @@ -99,6 +99,7 @@ function createRegExpForIgnorePatterns(normalizedOptions) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/class-methods-use-this.js b/eslint/lib/rules/class-methods-use-this.js index beb742d..1af6084 100644 --- a/eslint/lib/rules/class-methods-use-this.js +++ b/eslint/lib/rules/class-methods-use-this.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/comma-dangle.js b/eslint/lib/rules/comma-dangle.js index e97a598..232b27c 100644 --- a/eslint/lib/rules/comma-dangle.js +++ b/eslint/lib/rules/comma-dangle.js @@ -70,6 +70,7 @@ function normalizeOptions(optionValue, ecmaVersion) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/comma-spacing.js b/eslint/lib/rules/comma-spacing.js index d30a5ef..23a5175 100644 --- a/eslint/lib/rules/comma-spacing.js +++ b/eslint/lib/rules/comma-spacing.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/comma-style.js b/eslint/lib/rules/comma-style.js index 1d62fcf..cbcbe3a 100644 --- a/eslint/lib/rules/comma-style.js +++ b/eslint/lib/rules/comma-style.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/complexity.js b/eslint/lib/rules/complexity.js index a247039..b235555 100644 --- a/eslint/lib/rules/complexity.js +++ b/eslint/lib/rules/complexity.js @@ -17,6 +17,7 @@ const { upperCaseFirst } = require("../shared/string-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/computed-property-spacing.js b/eslint/lib/rules/computed-property-spacing.js index c8d8834..4850a8b 100644 --- a/eslint/lib/rules/computed-property-spacing.js +++ b/eslint/lib/rules/computed-property-spacing.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/consistent-return.js b/eslint/lib/rules/consistent-return.js index b509c36..fffb435 100644 --- a/eslint/lib/rules/consistent-return.js +++ b/eslint/lib/rules/consistent-return.js @@ -40,6 +40,7 @@ function isClassConstructor(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/consistent-this.js b/eslint/lib/rules/consistent-this.js index 025f3d0..a613f99 100644 --- a/eslint/lib/rules/consistent-this.js +++ b/eslint/lib/rules/consistent-this.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/constructor-super.js b/eslint/lib/rules/constructor-super.js index 38eb489..defdb91 100644 --- a/eslint/lib/rules/constructor-super.js +++ b/eslint/lib/rules/constructor-super.js @@ -116,6 +116,7 @@ function isPossibleConstructor(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/curly.js b/eslint/lib/rules/curly.js index 57c2e72..29e7395 100644 --- a/eslint/lib/rules/curly.js +++ b/eslint/lib/rules/curly.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/default-case-last.js b/eslint/lib/rules/default-case-last.js index 1eeadd1..34be289 100644 --- a/eslint/lib/rules/default-case-last.js +++ b/eslint/lib/rules/default-case-last.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/default-case.js b/eslint/lib/rules/default-case.js index b839aa2..6ce2385 100644 --- a/eslint/lib/rules/default-case.js +++ b/eslint/lib/rules/default-case.js @@ -10,6 +10,7 @@ const DEFAULT_COMMENT_PATTERN = /^no default$/iu; // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/default-param-last.js b/eslint/lib/rules/default-param-last.js index 8382d46..ea12a2a 100644 --- a/eslint/lib/rules/default-param-last.js +++ b/eslint/lib/rules/default-param-last.js @@ -5,6 +5,7 @@ "use strict"; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/dot-location.js b/eslint/lib/rules/dot-location.js index d80f870..9dea4f2 100644 --- a/eslint/lib/rules/dot-location.js +++ b/eslint/lib/rules/dot-location.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/dot-notation.js b/eslint/lib/rules/dot-notation.js index 1cd908f..6f6b516 100644 --- a/eslint/lib/rules/dot-notation.js +++ b/eslint/lib/rules/dot-notation.js @@ -20,6 +20,7 @@ const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/u; // `null` literal must be handled separately. const literalTypesToCheck = new Set(["string", "boolean"]); +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/eol-last.js b/eslint/lib/rules/eol-last.js index f8b922c..393b934 100644 --- a/eslint/lib/rules/eol-last.js +++ b/eslint/lib/rules/eol-last.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/eqeqeq.js b/eslint/lib/rules/eqeqeq.js index d3e6b5a..b5d784d 100644 --- a/eslint/lib/rules/eqeqeq.js +++ b/eslint/lib/rules/eqeqeq.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/for-direction.js b/eslint/lib/rules/for-direction.js index abe4ad3..d3d825a 100644 --- a/eslint/lib/rules/for-direction.js +++ b/eslint/lib/rules/for-direction.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/func-call-spacing.js b/eslint/lib/rules/func-call-spacing.js index a6ebde4..0391d99 100644 --- a/eslint/lib/rules/func-call-spacing.js +++ b/eslint/lib/rules/func-call-spacing.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/func-name-matching.js b/eslint/lib/rules/func-name-matching.js index 122cfd8..9cee5fe 100644 --- a/eslint/lib/rules/func-name-matching.js +++ b/eslint/lib/rules/func-name-matching.js @@ -68,6 +68,7 @@ const optionsObject = { additionalProperties: false }; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/func-names.js b/eslint/lib/rules/func-names.js index 589903c..c7b2072 100644 --- a/eslint/lib/rules/func-names.js +++ b/eslint/lib/rules/func-names.js @@ -24,6 +24,7 @@ function isFunctionName(variable) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/func-style.js b/eslint/lib/rules/func-style.js index 0921ff5..f715748 100644 --- a/eslint/lib/rules/func-style.js +++ b/eslint/lib/rules/func-style.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/function-call-argument-newline.js b/eslint/lib/rules/function-call-argument-newline.js index ed4e296..f3cfeee 100644 --- a/eslint/lib/rules/function-call-argument-newline.js +++ b/eslint/lib/rules/function-call-argument-newline.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/function-paren-newline.js b/eslint/lib/rules/function-paren-newline.js index 18435b7..ed94fad 100644 --- a/eslint/lib/rules/function-paren-newline.js +++ b/eslint/lib/rules/function-paren-newline.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/generator-star-spacing.js b/eslint/lib/rules/generator-star-spacing.js index c50445c..28e8101 100644 --- a/eslint/lib/rules/generator-star-spacing.js +++ b/eslint/lib/rules/generator-star-spacing.js @@ -25,6 +25,7 @@ const OVERRIDE_SCHEMA = { ] }; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/getter-return.js b/eslint/lib/rules/getter-return.js index 8bb4253..03cfce2 100644 --- a/eslint/lib/rules/getter-return.js +++ b/eslint/lib/rules/getter-return.js @@ -29,6 +29,7 @@ function isReachable(segment) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/global-require.js b/eslint/lib/rules/global-require.js index f2d29d1..f00ca9b 100644 --- a/eslint/lib/rules/global-require.js +++ b/eslint/lib/rules/global-require.js @@ -48,6 +48,7 @@ function isShadowed(scope, node) { return reference && reference.resolved && reference.resolved.defs.length > 0; } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/grouped-accessor-pairs.js b/eslint/lib/rules/grouped-accessor-pairs.js index cc4a4b5..0fe6f91 100644 --- a/eslint/lib/rules/grouped-accessor-pairs.js +++ b/eslint/lib/rules/grouped-accessor-pairs.js @@ -90,6 +90,7 @@ function isAccessorKind(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/guard-for-in.js b/eslint/lib/rules/guard-for-in.js index 6f877ba..1c52af7 100644 --- a/eslint/lib/rules/guard-for-in.js +++ b/eslint/lib/rules/guard-for-in.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/handle-callback-err.js b/eslint/lib/rules/handle-callback-err.js index cdb3a76..f370407 100644 --- a/eslint/lib/rules/handle-callback-err.js +++ b/eslint/lib/rules/handle-callback-err.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/id-blacklist.js b/eslint/lib/rules/id-blacklist.js index 77deac7..f7e04ae 100644 --- a/eslint/lib/rules/id-blacklist.js +++ b/eslint/lib/rules/id-blacklist.js @@ -110,6 +110,7 @@ function isShorthandPropertyDefinition(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/id-denylist.js b/eslint/lib/rules/id-denylist.js index 2b34635..a0b1f41 100644 --- a/eslint/lib/rules/id-denylist.js +++ b/eslint/lib/rules/id-denylist.js @@ -93,6 +93,7 @@ function isPropertyNameInDestructuring(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/id-length.js b/eslint/lib/rules/id-length.js index ac6385f..3701c66 100644 --- a/eslint/lib/rules/id-length.js +++ b/eslint/lib/rules/id-length.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/id-match.js b/eslint/lib/rules/id-match.js index 7a6cd05..bcc07a8 100644 --- a/eslint/lib/rules/id-match.js +++ b/eslint/lib/rules/id-match.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/implicit-arrow-linebreak.js b/eslint/lib/rules/implicit-arrow-linebreak.js index 2d09552..71b2437 100644 --- a/eslint/lib/rules/implicit-arrow-linebreak.js +++ b/eslint/lib/rules/implicit-arrow-linebreak.js @@ -9,6 +9,7 @@ const { isCommentToken, isNotOpeningParenToken } = require("./utils/ast-utils"); //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/indent-legacy.js b/eslint/lib/rules/indent-legacy.js index 54ca9dd..bd61f6f 100644 --- a/eslint/lib/rules/indent-legacy.js +++ b/eslint/lib/rules/indent-legacy.js @@ -20,6 +20,7 @@ const astUtils = require("./utils/ast-utils"); //------------------------------------------------------------------------------ /* istanbul ignore next: this rule has known coverage issues, but it's deprecated and shouldn't be updated in the future anyway. */ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/indent.js b/eslint/lib/rules/indent.js index f4dbfff..6152bc5 100644 --- a/eslint/lib/rules/indent.js +++ b/eslint/lib/rules/indent.js @@ -494,6 +494,7 @@ const ELEMENT_LIST_SCHEMA = { ] }; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/init-declarations.js b/eslint/lib/rules/init-declarations.js index d994bbc..d952b89 100644 --- a/eslint/lib/rules/init-declarations.js +++ b/eslint/lib/rules/init-declarations.js @@ -42,6 +42,7 @@ function isInitialized(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/jsx-quotes.js b/eslint/lib/rules/jsx-quotes.js index cbadc19..a0cdfaa 100644 --- a/eslint/lib/rules/jsx-quotes.js +++ b/eslint/lib/rules/jsx-quotes.js @@ -36,6 +36,7 @@ const QUOTE_SETTINGS = { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/key-spacing.js b/eslint/lib/rules/key-spacing.js index c09cebb..ce8bad5 100644 --- a/eslint/lib/rules/key-spacing.js +++ b/eslint/lib/rules/key-spacing.js @@ -133,6 +133,7 @@ function initOptions(toOptions, fromOptions) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/keyword-spacing.js b/eslint/lib/rules/keyword-spacing.js index 44222e1..956fc42 100644 --- a/eslint/lib/rules/keyword-spacing.js +++ b/eslint/lib/rules/keyword-spacing.js @@ -61,6 +61,7 @@ function isCloseParenOfTemplate(token) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/line-comment-position.js b/eslint/lib/rules/line-comment-position.js index ad109a4..9ce2831 100644 --- a/eslint/lib/rules/line-comment-position.js +++ b/eslint/lib/rules/line-comment-position.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/linebreak-style.js b/eslint/lib/rules/linebreak-style.js index 92996eb..483788a 100644 --- a/eslint/lib/rules/linebreak-style.js +++ b/eslint/lib/rules/linebreak-style.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/lines-around-comment.js b/eslint/lib/rules/lines-around-comment.js index 513d196..74df09b 100644 --- a/eslint/lib/rules/lines-around-comment.js +++ b/eslint/lib/rules/lines-around-comment.js @@ -49,6 +49,7 @@ function getCommentLineNums(comments) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/lines-around-directive.js b/eslint/lib/rules/lines-around-directive.js index c0c70e1..21884f1 100644 --- a/eslint/lib/rules/lines-around-directive.js +++ b/eslint/lib/rules/lines-around-directive.js @@ -12,6 +12,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/lines-between-class-members.js b/eslint/lib/rules/lines-between-class-members.js index e4c05f3..1d6b7e7 100644 --- a/eslint/lib/rules/lines-between-class-members.js +++ b/eslint/lib/rules/lines-between-class-members.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/max-classes-per-file.js b/eslint/lib/rules/max-classes-per-file.js index 3d26108..2157beb 100644 --- a/eslint/lib/rules/max-classes-per-file.js +++ b/eslint/lib/rules/max-classes-per-file.js @@ -13,6 +13,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/max-depth.js b/eslint/lib/rules/max-depth.js index bdacc66..8006ffd 100644 --- a/eslint/lib/rules/max-depth.js +++ b/eslint/lib/rules/max-depth.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/max-len.js b/eslint/lib/rules/max-len.js index 8c7985d..d05559e 100644 --- a/eslint/lib/rules/max-len.js +++ b/eslint/lib/rules/max-len.js @@ -63,6 +63,7 @@ const OPTIONS_OR_INTEGER_SCHEMA = { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/max-lines-per-function.js b/eslint/lib/rules/max-lines-per-function.js index b2130ca..c1b945c 100644 --- a/eslint/lib/rules/max-lines-per-function.js +++ b/eslint/lib/rules/max-lines-per-function.js @@ -65,6 +65,7 @@ function getCommentLineNumbers(comments) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", @@ -79,7 +80,7 @@ module.exports = { OPTIONS_OR_INTEGER_SCHEMA ], messages: { - exceed: "{{name}} has too many lines ({{lineCount}}). Maximum allowed is {{maxLines}}." + exceed: "{{name}} has exceeded the limit of lines allowed by {{linesExceed}}. Maximum allowed number of lines per function is {{maxLines}}." } }, @@ -169,18 +170,26 @@ module.exports = { return; } let lineCount = 0; + let comments = 0; + let blankLines = 0; for (let i = node.loc.start.line - 1; i < node.loc.end.line; ++i) { const line = lines[i]; if (skipComments) { if (commentLineNumbers.has(i + 1) && isFullLineComment(line, i + 1, commentLineNumbers.get(i + 1))) { + if (lineCount <= maxLines) { + comments++; + } continue; } } if (skipBlankLines) { if (line.match(/^\s*$/u)) { + if (lineCount <= maxLines) { + blankLines++; + } continue; } } @@ -190,11 +199,21 @@ module.exports = { if (lineCount > maxLines) { const name = upperCaseFirst(astUtils.getFunctionNameWithKind(funcNode)); + const linesExceed = lineCount - maxLines; + + const loc = { + start: { + line: node.loc.start.line + maxLines + (comments + blankLines), + column: 0 + }, + end: node.loc.end + }; context.report({ node, + loc, messageId: "exceed", - data: { name, lineCount, maxLines } + data: { name, linesExceed, maxLines } }); } } diff --git a/eslint/lib/rules/max-lines.js b/eslint/lib/rules/max-lines.js index 291d7d9..31fc657 100644 --- a/eslint/lib/rules/max-lines.js +++ b/eslint/lib/rules/max-lines.js @@ -28,6 +28,7 @@ function range(start, end) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/max-nested-callbacks.js b/eslint/lib/rules/max-nested-callbacks.js index df24a96..0d43050 100644 --- a/eslint/lib/rules/max-nested-callbacks.js +++ b/eslint/lib/rules/max-nested-callbacks.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/max-params.js b/eslint/lib/rules/max-params.js index c8be60e..72379d2 100644 --- a/eslint/lib/rules/max-params.js +++ b/eslint/lib/rules/max-params.js @@ -16,6 +16,7 @@ const { upperCaseFirst } = require("../shared/string-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/max-statements-per-line.js b/eslint/lib/rules/max-statements-per-line.js index 7c74329..61b508c 100644 --- a/eslint/lib/rules/max-statements-per-line.js +++ b/eslint/lib/rules/max-statements-per-line.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/max-statements.js b/eslint/lib/rules/max-statements.js index 969e40b..ac117e9 100644 --- a/eslint/lib/rules/max-statements.js +++ b/eslint/lib/rules/max-statements.js @@ -16,6 +16,7 @@ const { upperCaseFirst } = require("../shared/string-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/multiline-comment-style.js b/eslint/lib/rules/multiline-comment-style.js index da5ee50..7985bc8 100644 --- a/eslint/lib/rules/multiline-comment-style.js +++ b/eslint/lib/rules/multiline-comment-style.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/multiline-ternary.js b/eslint/lib/rules/multiline-ternary.js index 6f468c8..91aa5a1 100644 --- a/eslint/lib/rules/multiline-ternary.js +++ b/eslint/lib/rules/multiline-ternary.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/new-cap.js b/eslint/lib/rules/new-cap.js index 9abf337..466cfd4 100644 --- a/eslint/lib/rules/new-cap.js +++ b/eslint/lib/rules/new-cap.js @@ -76,6 +76,7 @@ function calculateCapIsNewExceptions(config) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/new-parens.js b/eslint/lib/rules/new-parens.js index 786300d..8ee4a2e 100644 --- a/eslint/lib/rules/new-parens.js +++ b/eslint/lib/rules/new-parens.js @@ -19,6 +19,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/newline-after-var.js b/eslint/lib/rules/newline-after-var.js index 3eea1b1..e519a3a 100644 --- a/eslint/lib/rules/newline-after-var.js +++ b/eslint/lib/rules/newline-after-var.js @@ -16,6 +16,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/newline-before-return.js b/eslint/lib/rules/newline-before-return.js index fd6341e..f4aa516 100644 --- a/eslint/lib/rules/newline-before-return.js +++ b/eslint/lib/rules/newline-before-return.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/newline-per-chained-call.js b/eslint/lib/rules/newline-per-chained-call.js index 8de9a6a..818bf70 100644 --- a/eslint/lib/rules/newline-per-chained-call.js +++ b/eslint/lib/rules/newline-per-chained-call.js @@ -12,6 +12,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-alert.js b/eslint/lib/rules/no-alert.js index 918b984..c6f7ddf 100644 --- a/eslint/lib/rules/no-alert.js +++ b/eslint/lib/rules/no-alert.js @@ -82,6 +82,7 @@ function isGlobalThisReferenceOrGlobalWindow(scope, node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-array-constructor.js b/eslint/lib/rules/no-array-constructor.js index 0904fa6..02e6114 100644 --- a/eslint/lib/rules/no-array-constructor.js +++ b/eslint/lib/rules/no-array-constructor.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-async-promise-executor.js b/eslint/lib/rules/no-async-promise-executor.js index 27116f1..f940f15 100644 --- a/eslint/lib/rules/no-async-promise-executor.js +++ b/eslint/lib/rules/no-async-promise-executor.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-await-in-loop.js b/eslint/lib/rules/no-await-in-loop.js index 38af8b5..3aea39a 100644 --- a/eslint/lib/rules/no-await-in-loop.js +++ b/eslint/lib/rules/no-await-in-loop.js @@ -53,6 +53,7 @@ function isLooped(node, parent) { } } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-bitwise.js b/eslint/lib/rules/no-bitwise.js index 10bf24a..1de1619 100644 --- a/eslint/lib/rules/no-bitwise.js +++ b/eslint/lib/rules/no-bitwise.js @@ -20,6 +20,7 @@ const BITWISE_OPERATORS = [ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-buffer-constructor.js b/eslint/lib/rules/no-buffer-constructor.js index cc5906e..678d703 100644 --- a/eslint/lib/rules/no-buffer-constructor.js +++ b/eslint/lib/rules/no-buffer-constructor.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/no-caller.js b/eslint/lib/rules/no-caller.js index dbb5279..a6ad94f 100644 --- a/eslint/lib/rules/no-caller.js +++ b/eslint/lib/rules/no-caller.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-case-declarations.js b/eslint/lib/rules/no-case-declarations.js index a132f03..d722f0c 100644 --- a/eslint/lib/rules/no-case-declarations.js +++ b/eslint/lib/rules/no-case-declarations.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-catch-shadow.js b/eslint/lib/rules/no-catch-shadow.js index 0cbeedf..d09c913 100644 --- a/eslint/lib/rules/no-catch-shadow.js +++ b/eslint/lib/rules/no-catch-shadow.js @@ -16,6 +16,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-class-assign.js b/eslint/lib/rules/no-class-assign.js index 839ad03..f679d42 100644 --- a/eslint/lib/rules/no-class-assign.js +++ b/eslint/lib/rules/no-class-assign.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-compare-neg-zero.js b/eslint/lib/rules/no-compare-neg-zero.js index e8fdaa0..fb56b99 100644 --- a/eslint/lib/rules/no-compare-neg-zero.js +++ b/eslint/lib/rules/no-compare-neg-zero.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-cond-assign.js b/eslint/lib/rules/no-cond-assign.js index 42f75af..30d5b3b 100644 --- a/eslint/lib/rules/no-cond-assign.js +++ b/eslint/lib/rules/no-cond-assign.js @@ -28,6 +28,7 @@ const NODE_DESCRIPTIONS = { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-confusing-arrow.js b/eslint/lib/rules/no-confusing-arrow.js index fa87f40..7b736c1 100644 --- a/eslint/lib/rules/no-confusing-arrow.js +++ b/eslint/lib/rules/no-confusing-arrow.js @@ -25,6 +25,7 @@ function isConditional(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-console.js b/eslint/lib/rules/no-console.js index a5937cb..c0802e2 100644 --- a/eslint/lib/rules/no-console.js +++ b/eslint/lib/rules/no-console.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-const-assign.js b/eslint/lib/rules/no-const-assign.js index 6ca1b61..b5f7c37 100644 --- a/eslint/lib/rules/no-const-assign.js +++ b/eslint/lib/rules/no-const-assign.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-constant-condition.js b/eslint/lib/rules/no-constant-condition.js index 7a7030a..0bcb319 100644 --- a/eslint/lib/rules/no-constant-condition.js +++ b/eslint/lib/rules/no-constant-condition.js @@ -13,6 +13,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", @@ -138,6 +139,7 @@ module.exports = { case "ArrowFunctionExpression": case "FunctionExpression": case "ObjectExpression": + case "ClassExpression": return true; case "TemplateLiteral": return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) || @@ -179,7 +181,8 @@ module.exports = { isLeftShortCircuit || isRightShortCircuit; } - + case "NewExpression": + return inBooleanPosition; case "AssignmentExpression": if (node.operator === "=") { return isConstant(node.right, inBooleanPosition); diff --git a/eslint/lib/rules/no-constructor-return.js b/eslint/lib/rules/no-constructor-return.js index b4b5baf..f8a717c 100644 --- a/eslint/lib/rules/no-constructor-return.js +++ b/eslint/lib/rules/no-constructor-return.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-continue.js b/eslint/lib/rules/no-continue.js index e72e862..8658a79 100644 --- a/eslint/lib/rules/no-continue.js +++ b/eslint/lib/rules/no-continue.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-control-regex.js b/eslint/lib/rules/no-control-regex.js index 908d61a..04f3449 100644 --- a/eslint/lib/rules/no-control-regex.js +++ b/eslint/lib/rules/no-control-regex.js @@ -46,6 +46,7 @@ const collector = new (class { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-debugger.js b/eslint/lib/rules/no-debugger.js index 46dd576..e62db1b 100644 --- a/eslint/lib/rules/no-debugger.js +++ b/eslint/lib/rules/no-debugger.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-delete-var.js b/eslint/lib/rules/no-delete-var.js index 1438ebc..1d1c710 100644 --- a/eslint/lib/rules/no-delete-var.js +++ b/eslint/lib/rules/no-delete-var.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-div-regex.js b/eslint/lib/rules/no-div-regex.js index 40388c3..175f6c2 100644 --- a/eslint/lib/rules/no-div-regex.js +++ b/eslint/lib/rules/no-div-regex.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-dupe-args.js b/eslint/lib/rules/no-dupe-args.js index 0880b9c..13090e1 100644 --- a/eslint/lib/rules/no-dupe-args.js +++ b/eslint/lib/rules/no-dupe-args.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-dupe-class-members.js b/eslint/lib/rules/no-dupe-class-members.js index f74865b..ae61f16 100644 --- a/eslint/lib/rules/no-dupe-class-members.js +++ b/eslint/lib/rules/no-dupe-class-members.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-dupe-else-if.js b/eslint/lib/rules/no-dupe-else-if.js index 0d8b17c..1e1d549 100644 --- a/eslint/lib/rules/no-dupe-else-if.js +++ b/eslint/lib/rules/no-dupe-else-if.js @@ -46,6 +46,7 @@ const splitByAnd = splitByLogicalOperator.bind(null, "&&"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-dupe-keys.js b/eslint/lib/rules/no-dupe-keys.js index ecec022..dac13cf 100644 --- a/eslint/lib/rules/no-dupe-keys.js +++ b/eslint/lib/rules/no-dupe-keys.js @@ -82,6 +82,7 @@ class ObjectInfo { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-duplicate-case.js b/eslint/lib/rules/no-duplicate-case.js index 4669dce..a0c0b31 100644 --- a/eslint/lib/rules/no-duplicate-case.js +++ b/eslint/lib/rules/no-duplicate-case.js @@ -16,6 +16,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-duplicate-imports.js b/eslint/lib/rules/no-duplicate-imports.js index 2663698..947bb30 100644 --- a/eslint/lib/rules/no-duplicate-imports.js +++ b/eslint/lib/rules/no-duplicate-imports.js @@ -227,6 +227,7 @@ function handleImportsExports( }; } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-else-return.js b/eslint/lib/rules/no-else-return.js index 4c981ae..3662fc8 100644 --- a/eslint/lib/rules/no-else-return.js +++ b/eslint/lib/rules/no-else-return.js @@ -16,6 +16,7 @@ const FixTracker = require("./utils/fix-tracker"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-empty-character-class.js b/eslint/lib/rules/no-empty-character-class.js index 85e8ef7..f75f591 100644 --- a/eslint/lib/rules/no-empty-character-class.js +++ b/eslint/lib/rules/no-empty-character-class.js @@ -24,6 +24,7 @@ const regex = /^([^\\[]|\\.|\[([^\\\]]|\\.)+\])*$/u; // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-empty-function.js b/eslint/lib/rules/no-empty-function.js index 8b1073a..6e73d63 100644 --- a/eslint/lib/rules/no-empty-function.js +++ b/eslint/lib/rules/no-empty-function.js @@ -89,6 +89,7 @@ function getKind(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-empty-pattern.js b/eslint/lib/rules/no-empty-pattern.js index 99ea3a7..2de575f 100644 --- a/eslint/lib/rules/no-empty-pattern.js +++ b/eslint/lib/rules/no-empty-pattern.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-empty.js b/eslint/lib/rules/no-empty.js index 4ed3c5c..f04ee2c 100644 --- a/eslint/lib/rules/no-empty.js +++ b/eslint/lib/rules/no-empty.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-eq-null.js b/eslint/lib/rules/no-eq-null.js index dae9228..b693737 100644 --- a/eslint/lib/rules/no-eq-null.js +++ b/eslint/lib/rules/no-eq-null.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-eval.js b/eslint/lib/rules/no-eval.js index 96b85a0..ae6d71b 100644 --- a/eslint/lib/rules/no-eval.js +++ b/eslint/lib/rules/no-eval.js @@ -37,6 +37,7 @@ function isMember(node, name) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-ex-assign.js b/eslint/lib/rules/no-ex-assign.js index cd56c94..3db1420 100644 --- a/eslint/lib/rules/no-ex-assign.js +++ b/eslint/lib/rules/no-ex-assign.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-extend-native.js b/eslint/lib/rules/no-extend-native.js index 4d5accb..771200f 100644 --- a/eslint/lib/rules/no-extend-native.js +++ b/eslint/lib/rules/no-extend-native.js @@ -16,6 +16,7 @@ const globals = require("globals"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-extra-bind.js b/eslint/lib/rules/no-extra-bind.js index 6fd3be1..561cb1a 100644 --- a/eslint/lib/rules/no-extra-bind.js +++ b/eslint/lib/rules/no-extra-bind.js @@ -20,6 +20,7 @@ const SIDE_EFFECT_FREE_NODE_TYPES = new Set(["Literal", "Identifier", "ThisExpre // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-extra-boolean-cast.js b/eslint/lib/rules/no-extra-boolean-cast.js index cb061da..f45ac61 100644 --- a/eslint/lib/rules/no-extra-boolean-cast.js +++ b/eslint/lib/rules/no-extra-boolean-cast.js @@ -18,6 +18,7 @@ const precedence = astUtils.getPrecedence; // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-extra-label.js b/eslint/lib/rules/no-extra-label.js index bbb2413..9186a9f 100644 --- a/eslint/lib/rules/no-extra-label.js +++ b/eslint/lib/rules/no-extra-label.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-extra-parens.js b/eslint/lib/rules/no-extra-parens.js index 0756d2f..5b54ae2 100644 --- a/eslint/lib/rules/no-extra-parens.js +++ b/eslint/lib/rules/no-extra-parens.js @@ -11,6 +11,7 @@ const { isParenthesized: isParenthesizedRaw } = require("eslint-utils"); const astUtils = require("./utils/ast-utils.js"); +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-extra-semi.js b/eslint/lib/rules/no-extra-semi.js index 0e2bcaf..89d9fce 100644 --- a/eslint/lib/rules/no-extra-semi.js +++ b/eslint/lib/rules/no-extra-semi.js @@ -16,6 +16,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-fallthrough.js b/eslint/lib/rules/no-fallthrough.js index bf2c825..f3b7c85 100644 --- a/eslint/lib/rules/no-fallthrough.js +++ b/eslint/lib/rules/no-fallthrough.js @@ -58,6 +58,7 @@ function hasBlankLinesBetween(node, token) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-floating-decimal.js b/eslint/lib/rules/no-floating-decimal.js index 92ac232..8831bb8 100644 --- a/eslint/lib/rules/no-floating-decimal.js +++ b/eslint/lib/rules/no-floating-decimal.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-func-assign.js b/eslint/lib/rules/no-func-assign.js index aa04f33..04a7dd3 100644 --- a/eslint/lib/rules/no-func-assign.js +++ b/eslint/lib/rules/no-func-assign.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-global-assign.js b/eslint/lib/rules/no-global-assign.js index 85aac7c..340c6c7 100644 --- a/eslint/lib/rules/no-global-assign.js +++ b/eslint/lib/rules/no-global-assign.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-implicit-coercion.js b/eslint/lib/rules/no-implicit-coercion.js index 1d11e10..f646093 100644 --- a/eslint/lib/rules/no-implicit-coercion.js +++ b/eslint/lib/rules/no-implicit-coercion.js @@ -167,6 +167,7 @@ function getNonEmptyOperand(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-implicit-globals.js b/eslint/lib/rules/no-implicit-globals.js index 8740cd8..5dd6aa7 100644 --- a/eslint/lib/rules/no-implicit-globals.js +++ b/eslint/lib/rules/no-implicit-globals.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-implied-eval.js b/eslint/lib/rules/no-implied-eval.js index 2432e68..38de5b3 100644 --- a/eslint/lib/rules/no-implied-eval.js +++ b/eslint/lib/rules/no-implied-eval.js @@ -16,6 +16,7 @@ const { getStaticValue } = require("eslint-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-import-assign.js b/eslint/lib/rules/no-import-assign.js index fbe63d0..385386e 100644 --- a/eslint/lib/rules/no-import-assign.js +++ b/eslint/lib/rules/no-import-assign.js @@ -174,6 +174,7 @@ function getWriteNode(id) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-inline-comments.js b/eslint/lib/rules/no-inline-comments.js index 8a955a6..2ed7feb 100644 --- a/eslint/lib/rules/no-inline-comments.js +++ b/eslint/lib/rules/no-inline-comments.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-inner-declarations.js b/eslint/lib/rules/no-inner-declarations.js index 49b5114..3b0feb7 100644 --- a/eslint/lib/rules/no-inner-declarations.js +++ b/eslint/lib/rules/no-inner-declarations.js @@ -42,6 +42,7 @@ function getAllowedBodyDescription(node) { return "program"; } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-invalid-regexp.js b/eslint/lib/rules/no-invalid-regexp.js index ee19932..92ac512 100644 --- a/eslint/lib/rules/no-invalid-regexp.js +++ b/eslint/lib/rules/no-invalid-regexp.js @@ -17,6 +17,7 @@ const undefined1 = void 0; // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-invalid-this.js b/eslint/lib/rules/no-invalid-this.js index e1d7cbc..5f9b9f8 100644 --- a/eslint/lib/rules/no-invalid-this.js +++ b/eslint/lib/rules/no-invalid-this.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-irregular-whitespace.js b/eslint/lib/rules/no-irregular-whitespace.js index c160971..65c4d67 100644 --- a/eslint/lib/rules/no-irregular-whitespace.js +++ b/eslint/lib/rules/no-irregular-whitespace.js @@ -25,6 +25,7 @@ const LINE_BREAK = astUtils.createGlobalLinebreakMatcher(); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-iterator.js b/eslint/lib/rules/no-iterator.js index 4117f62..d112672 100644 --- a/eslint/lib/rules/no-iterator.js +++ b/eslint/lib/rules/no-iterator.js @@ -15,6 +15,7 @@ const { getStaticPropertyName } = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-label-var.js b/eslint/lib/rules/no-label-var.js index 4532527..5021181 100644 --- a/eslint/lib/rules/no-label-var.js +++ b/eslint/lib/rules/no-label-var.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-labels.js b/eslint/lib/rules/no-labels.js index 5dd15be..2e79f37 100644 --- a/eslint/lib/rules/no-labels.js +++ b/eslint/lib/rules/no-labels.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-lone-blocks.js b/eslint/lib/rules/no-lone-blocks.js index 33d4706..f9fe951 100644 --- a/eslint/lib/rules/no-lone-blocks.js +++ b/eslint/lib/rules/no-lone-blocks.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-lonely-if.js b/eslint/lib/rules/no-lonely-if.js index e44f000..9abd465 100644 --- a/eslint/lib/rules/no-lonely-if.js +++ b/eslint/lib/rules/no-lonely-if.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-loop-func.js b/eslint/lib/rules/no-loop-func.js index d1a7868..c546061 100644 --- a/eslint/lib/rules/no-loop-func.js +++ b/eslint/lib/rules/no-loop-func.js @@ -148,6 +148,7 @@ function isSafe(loopNode, reference) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-loss-of-precision.js b/eslint/lib/rules/no-loss-of-precision.js index 417616d..fefc7b7 100644 --- a/eslint/lib/rules/no-loss-of-precision.js +++ b/eslint/lib/rules/no-loss-of-precision.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-magic-numbers.js b/eslint/lib/rules/no-magic-numbers.js index a2c678e..dfcd593 100644 --- a/eslint/lib/rules/no-magic-numbers.js +++ b/eslint/lib/rules/no-magic-numbers.js @@ -26,6 +26,7 @@ function normalizeIgnoreValue(x) { return x; } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-misleading-character-class.js b/eslint/lib/rules/no-misleading-character-class.js index 70e31e6..94b2878 100644 --- a/eslint/lib/rules/no-misleading-character-class.js +++ b/eslint/lib/rules/no-misleading-character-class.js @@ -98,6 +98,7 @@ const kinds = Object.keys(hasCharacterSequence); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-mixed-operators.js b/eslint/lib/rules/no-mixed-operators.js index ed37a90..c75d011 100644 --- a/eslint/lib/rules/no-mixed-operators.js +++ b/eslint/lib/rules/no-mixed-operators.js @@ -82,6 +82,7 @@ function getChildNode(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-mixed-requires.js b/eslint/lib/rules/no-mixed-requires.js index f7c2d11..5e05cf7 100644 --- a/eslint/lib/rules/no-mixed-requires.js +++ b/eslint/lib/rules/no-mixed-requires.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/no-mixed-spaces-and-tabs.js b/eslint/lib/rules/no-mixed-spaces-and-tabs.js index ac73cdd..f82a352 100644 --- a/eslint/lib/rules/no-mixed-spaces-and-tabs.js +++ b/eslint/lib/rules/no-mixed-spaces-and-tabs.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-multi-assign.js b/eslint/lib/rules/no-multi-assign.js index 8d7bd32..be4d41f 100644 --- a/eslint/lib/rules/no-multi-assign.js +++ b/eslint/lib/rules/no-multi-assign.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-multi-spaces.js b/eslint/lib/rules/no-multi-spaces.js index 0134dd2..e37aae0 100644 --- a/eslint/lib/rules/no-multi-spaces.js +++ b/eslint/lib/rules/no-multi-spaces.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-multi-str.js b/eslint/lib/rules/no-multi-str.js index 848f8d4..6a17d58 100644 --- a/eslint/lib/rules/no-multi-str.js +++ b/eslint/lib/rules/no-multi-str.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-multiple-empty-lines.js b/eslint/lib/rules/no-multiple-empty-lines.js index 33ac76f..d012303 100644 --- a/eslint/lib/rules/no-multiple-empty-lines.js +++ b/eslint/lib/rules/no-multiple-empty-lines.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-native-reassign.js b/eslint/lib/rules/no-native-reassign.js index 80ba094..1814a5b 100644 --- a/eslint/lib/rules/no-native-reassign.js +++ b/eslint/lib/rules/no-native-reassign.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-negated-condition.js b/eslint/lib/rules/no-negated-condition.js index b5cbadc..a287013 100644 --- a/eslint/lib/rules/no-negated-condition.js +++ b/eslint/lib/rules/no-negated-condition.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-negated-in-lhs.js b/eslint/lib/rules/no-negated-in-lhs.js index 0f9c84b..95ab58a 100644 --- a/eslint/lib/rules/no-negated-in-lhs.js +++ b/eslint/lib/rules/no-negated-in-lhs.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-nested-ternary.js b/eslint/lib/rules/no-nested-ternary.js index 2d3359d..c87875e 100644 --- a/eslint/lib/rules/no-nested-ternary.js +++ b/eslint/lib/rules/no-nested-ternary.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-new-func.js b/eslint/lib/rules/no-new-func.js index ddf6102..3b59943 100644 --- a/eslint/lib/rules/no-new-func.js +++ b/eslint/lib/rules/no-new-func.js @@ -21,6 +21,7 @@ const callMethods = new Set(["apply", "bind", "call"]); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-new-object.js b/eslint/lib/rules/no-new-object.js index 17dfd34..1a5784d 100644 --- a/eslint/lib/rules/no-new-object.js +++ b/eslint/lib/rules/no-new-object.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-new-require.js b/eslint/lib/rules/no-new-require.js index 7973f8f..5dadf6c 100644 --- a/eslint/lib/rules/no-new-require.js +++ b/eslint/lib/rules/no-new-require.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/no-new-symbol.js b/eslint/lib/rules/no-new-symbol.js index 391527d..6acfca9 100644 --- a/eslint/lib/rules/no-new-symbol.js +++ b/eslint/lib/rules/no-new-symbol.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-new-wrappers.js b/eslint/lib/rules/no-new-wrappers.js index b697d8d..4667d58 100644 --- a/eslint/lib/rules/no-new-wrappers.js +++ b/eslint/lib/rules/no-new-wrappers.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-new.js b/eslint/lib/rules/no-new.js index 1b37f07..5b09765 100644 --- a/eslint/lib/rules/no-new.js +++ b/eslint/lib/rules/no-new.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-nonoctal-decimal-escape.js b/eslint/lib/rules/no-nonoctal-decimal-escape.js index da61f61..3edd269 100644 --- a/eslint/lib/rules/no-nonoctal-decimal-escape.js +++ b/eslint/lib/rules/no-nonoctal-decimal-escape.js @@ -24,6 +24,7 @@ function getUnicodeEscape(character) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-obj-calls.js b/eslint/lib/rules/no-obj-calls.js index d62c1f0..667ba69 100644 --- a/eslint/lib/rules/no-obj-calls.js +++ b/eslint/lib/rules/no-obj-calls.js @@ -37,6 +37,7 @@ function getReportNodeName(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-octal-escape.js b/eslint/lib/rules/no-octal-escape.js index 4513a83..9ac56ab 100644 --- a/eslint/lib/rules/no-octal-escape.js +++ b/eslint/lib/rules/no-octal-escape.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-octal.js b/eslint/lib/rules/no-octal.js index 5ee6895..44df45f 100644 --- a/eslint/lib/rules/no-octal.js +++ b/eslint/lib/rules/no-octal.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-param-reassign.js b/eslint/lib/rules/no-param-reassign.js index b758b9d..87a6b70 100644 --- a/eslint/lib/rules/no-param-reassign.js +++ b/eslint/lib/rules/no-param-reassign.js @@ -10,6 +10,7 @@ const stopNodePattern = /(?:Statement|Declaration|Function(?:Expression)?|Program)$/u; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-path-concat.js b/eslint/lib/rules/no-path-concat.js index 184c918..8d570a3 100644 --- a/eslint/lib/rules/no-path-concat.js +++ b/eslint/lib/rules/no-path-concat.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/no-plusplus.js b/eslint/lib/rules/no-plusplus.js index d7b6c73..2565da4 100644 --- a/eslint/lib/rules/no-plusplus.js +++ b/eslint/lib/rules/no-plusplus.js @@ -45,6 +45,7 @@ function isForLoopAfterthought(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-process-env.js b/eslint/lib/rules/no-process-env.js index c61b557..f7c2c71 100644 --- a/eslint/lib/rules/no-process-env.js +++ b/eslint/lib/rules/no-process-env.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/no-process-exit.js b/eslint/lib/rules/no-process-exit.js index 73310a9..251044b 100644 --- a/eslint/lib/rules/no-process-exit.js +++ b/eslint/lib/rules/no-process-exit.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/no-promise-executor-return.js b/eslint/lib/rules/no-promise-executor-return.js index 4265241..e40d4bc 100644 --- a/eslint/lib/rules/no-promise-executor-return.js +++ b/eslint/lib/rules/no-promise-executor-return.js @@ -63,6 +63,7 @@ function isPromiseExecutor(node, scope) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-proto.js b/eslint/lib/rules/no-proto.js index 0c2490f..e6659e5 100644 --- a/eslint/lib/rules/no-proto.js +++ b/eslint/lib/rules/no-proto.js @@ -15,6 +15,7 @@ const { getStaticPropertyName } = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-prototype-builtins.js b/eslint/lib/rules/no-prototype-builtins.js index 1f837b9..ae73698 100644 --- a/eslint/lib/rules/no-prototype-builtins.js +++ b/eslint/lib/rules/no-prototype-builtins.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-redeclare.js b/eslint/lib/rules/no-redeclare.js index 3de4397..cc71a61 100644 --- a/eslint/lib/rules/no-redeclare.js +++ b/eslint/lib/rules/no-redeclare.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-regex-spaces.js b/eslint/lib/rules/no-regex-spaces.js index 1d6b121..400c72b 100644 --- a/eslint/lib/rules/no-regex-spaces.js +++ b/eslint/lib/rules/no-regex-spaces.js @@ -33,6 +33,7 @@ function isString(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-restricted-exports.js b/eslint/lib/rules/no-restricted-exports.js index f568fdc..775e505 100644 --- a/eslint/lib/rules/no-restricted-exports.js +++ b/eslint/lib/rules/no-restricted-exports.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-restricted-globals.js b/eslint/lib/rules/no-restricted-globals.js index efbcd75..09d3478 100644 --- a/eslint/lib/rules/no-restricted-globals.js +++ b/eslint/lib/rules/no-restricted-globals.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-restricted-imports.js b/eslint/lib/rules/no-restricted-imports.js index eda6340..6813037 100644 --- a/eslint/lib/rules/no-restricted-imports.js +++ b/eslint/lib/rules/no-restricted-imports.js @@ -73,6 +73,7 @@ const arrayOfStringsOrObjectPatterns = { ] }; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-restricted-modules.js b/eslint/lib/rules/no-restricted-modules.js index 66e6fe4..26e75ef 100644 --- a/eslint/lib/rules/no-restricted-modules.js +++ b/eslint/lib/rules/no-restricted-modules.js @@ -39,6 +39,7 @@ const arrayOfStringsOrObjects = { uniqueItems: true }; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/no-restricted-properties.js b/eslint/lib/rules/no-restricted-properties.js index 3671d88..1e8c7a8 100644 --- a/eslint/lib/rules/no-restricted-properties.js +++ b/eslint/lib/rules/no-restricted-properties.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-restricted-syntax.js b/eslint/lib/rules/no-restricted-syntax.js index 0ff6b91..713d115 100644 --- a/eslint/lib/rules/no-restricted-syntax.js +++ b/eslint/lib/rules/no-restricted-syntax.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-return-assign.js b/eslint/lib/rules/no-return-assign.js index ecb789e..4fd7a3d 100644 --- a/eslint/lib/rules/no-return-assign.js +++ b/eslint/lib/rules/no-return-assign.js @@ -20,6 +20,7 @@ const SENTINEL_TYPE = /^(?:[a-zA-Z]+?Statement|ArrowFunctionExpression|FunctionE // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-return-await.js b/eslint/lib/rules/no-return-await.js index 7ec808f..191bf42 100644 --- a/eslint/lib/rules/no-return-await.js +++ b/eslint/lib/rules/no-return-await.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-script-url.js b/eslint/lib/rules/no-script-url.js index 12451ad..0eef254 100644 --- a/eslint/lib/rules/no-script-url.js +++ b/eslint/lib/rules/no-script-url.js @@ -12,6 +12,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-self-assign.js b/eslint/lib/rules/no-self-assign.js index 813771e..d9e261d 100644 --- a/eslint/lib/rules/no-self-assign.js +++ b/eslint/lib/rules/no-self-assign.js @@ -124,6 +124,7 @@ function eachSelfAssignment(left, right, props, report) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-self-compare.js b/eslint/lib/rules/no-self-compare.js index ee77ff0..c351289 100644 --- a/eslint/lib/rules/no-self-compare.js +++ b/eslint/lib/rules/no-self-compare.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-sequences.js b/eslint/lib/rules/no-sequences.js index b894125..376aec3 100644 --- a/eslint/lib/rules/no-sequences.js +++ b/eslint/lib/rules/no-sequences.js @@ -23,6 +23,7 @@ const DEFAULT_OPTIONS = { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-setter-return.js b/eslint/lib/rules/no-setter-return.js index 67114ad..7204e5c 100644 --- a/eslint/lib/rules/no-setter-return.js +++ b/eslint/lib/rules/no-setter-return.js @@ -136,6 +136,7 @@ function getOuterScope(scope) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-shadow-restricted-names.js b/eslint/lib/rules/no-shadow-restricted-names.js index 7d4174a..52620e5 100644 --- a/eslint/lib/rules/no-shadow-restricted-names.js +++ b/eslint/lib/rules/no-shadow-restricted-names.js @@ -21,6 +21,7 @@ function safelyShadowsUndefined(variable) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-shadow.js b/eslint/lib/rules/no-shadow.js index 4ec3576..bd61923 100644 --- a/eslint/lib/rules/no-shadow.js +++ b/eslint/lib/rules/no-shadow.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-spaced-func.js b/eslint/lib/rules/no-spaced-func.js index 8f51d54..1d29943 100644 --- a/eslint/lib/rules/no-spaced-func.js +++ b/eslint/lib/rules/no-spaced-func.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-sparse-arrays.js b/eslint/lib/rules/no-sparse-arrays.js index 56ce5dc..c95bc20 100644 --- a/eslint/lib/rules/no-sparse-arrays.js +++ b/eslint/lib/rules/no-sparse-arrays.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-sync.js b/eslint/lib/rules/no-sync.js index ea40df1..3536d9f 100644 --- a/eslint/lib/rules/no-sync.js +++ b/eslint/lib/rules/no-sync.js @@ -10,6 +10,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { deprecated: true, diff --git a/eslint/lib/rules/no-tabs.js b/eslint/lib/rules/no-tabs.js index 1f3921a..9758b85 100644 --- a/eslint/lib/rules/no-tabs.js +++ b/eslint/lib/rules/no-tabs.js @@ -16,6 +16,7 @@ const anyNonWhitespaceRegex = /\S/u; // Public Interface //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-template-curly-in-string.js b/eslint/lib/rules/no-template-curly-in-string.js index e71480f..1901460 100644 --- a/eslint/lib/rules/no-template-curly-in-string.js +++ b/eslint/lib/rules/no-template-curly-in-string.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-ternary.js b/eslint/lib/rules/no-ternary.js index 8b2e10a..b0dc626 100644 --- a/eslint/lib/rules/no-ternary.js +++ b/eslint/lib/rules/no-ternary.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-this-before-super.js b/eslint/lib/rules/no-this-before-super.js index 9cc85eb..929eded 100644 --- a/eslint/lib/rules/no-this-before-super.js +++ b/eslint/lib/rules/no-this-before-super.js @@ -34,6 +34,7 @@ function isConstructorFunction(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-throw-literal.js b/eslint/lib/rules/no-throw-literal.js index 311e6d4..c670ed9 100644 --- a/eslint/lib/rules/no-throw-literal.js +++ b/eslint/lib/rules/no-throw-literal.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-trailing-spaces.js b/eslint/lib/rules/no-trailing-spaces.js index 1930098..9e720ad 100644 --- a/eslint/lib/rules/no-trailing-spaces.js +++ b/eslint/lib/rules/no-trailing-spaces.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-undef-init.js b/eslint/lib/rules/no-undef-init.js index 7298d34..2c2204c 100644 --- a/eslint/lib/rules/no-undef-init.js +++ b/eslint/lib/rules/no-undef-init.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-undef.js b/eslint/lib/rules/no-undef.js index ee611f9..f659032 100644 --- a/eslint/lib/rules/no-undef.js +++ b/eslint/lib/rules/no-undef.js @@ -23,6 +23,7 @@ function hasTypeOfOperator(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-undefined.js b/eslint/lib/rules/no-undefined.js index ad30225..de396d8 100644 --- a/eslint/lib/rules/no-undefined.js +++ b/eslint/lib/rules/no-undefined.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-underscore-dangle.js b/eslint/lib/rules/no-underscore-dangle.js index 916b8c0..0ab41fe 100644 --- a/eslint/lib/rules/no-underscore-dangle.js +++ b/eslint/lib/rules/no-underscore-dangle.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-unexpected-multiline.js b/eslint/lib/rules/no-unexpected-multiline.js index 4447959..60d8f31 100644 --- a/eslint/lib/rules/no-unexpected-multiline.js +++ b/eslint/lib/rules/no-unexpected-multiline.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-unmodified-loop-condition.js b/eslint/lib/rules/no-unmodified-loop-condition.js index ba321d2..5b8da26 100644 --- a/eslint/lib/rules/no-unmodified-loop-condition.js +++ b/eslint/lib/rules/no-unmodified-loop-condition.js @@ -156,6 +156,7 @@ function updateModifiedFlag(conditions, modifiers) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-unneeded-ternary.js b/eslint/lib/rules/no-unneeded-ternary.js index e12240d..e00d527 100644 --- a/eslint/lib/rules/no-unneeded-ternary.js +++ b/eslint/lib/rules/no-unneeded-ternary.js @@ -23,6 +23,7 @@ const OR_PRECEDENCE = astUtils.getPrecedence({ type: "LogicalExpression", operat // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-unreachable-loop.js b/eslint/lib/rules/no-unreachable-loop.js index 5cbfac4..f100263 100644 --- a/eslint/lib/rules/no-unreachable-loop.js +++ b/eslint/lib/rules/no-unreachable-loop.js @@ -53,6 +53,7 @@ function getDifference(arrA, arrB) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-unreachable.js b/eslint/lib/rules/no-unreachable.js index ce17a59..4dda51f 100644 --- a/eslint/lib/rules/no-unreachable.js +++ b/eslint/lib/rules/no-unreachable.js @@ -105,6 +105,7 @@ class ConsecutiveRange { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-unsafe-finally.js b/eslint/lib/rules/no-unsafe-finally.js index 4bb7f7f..26c05ea 100644 --- a/eslint/lib/rules/no-unsafe-finally.js +++ b/eslint/lib/rules/no-unsafe-finally.js @@ -18,6 +18,7 @@ const SENTINEL_NODE_TYPE_CONTINUE = /^(?:Program|(?:Function|Class)(?:Declaratio // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-unsafe-negation.js b/eslint/lib/rules/no-unsafe-negation.js index c681986..057b174 100644 --- a/eslint/lib/rules/no-unsafe-negation.js +++ b/eslint/lib/rules/no-unsafe-negation.js @@ -46,6 +46,7 @@ function isNegation(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-unsafe-optional-chaining.js b/eslint/lib/rules/no-unsafe-optional-chaining.js index cc15c99..8556ccb 100644 --- a/eslint/lib/rules/no-unsafe-optional-chaining.js +++ b/eslint/lib/rules/no-unsafe-optional-chaining.js @@ -18,6 +18,7 @@ function isDestructuringPattern(node) { return node.type === "ObjectPattern" || node.type === "ArrayPattern"; } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-unused-expressions.js b/eslint/lib/rules/no-unused-expressions.js index 2081a51..68a7ebd 100644 --- a/eslint/lib/rules/no-unused-expressions.js +++ b/eslint/lib/rules/no-unused-expressions.js @@ -24,6 +24,7 @@ function alwaysFalse() { return false; } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-unused-labels.js b/eslint/lib/rules/no-unused-labels.js index f0b0961..f309dd1 100644 --- a/eslint/lib/rules/no-unused-labels.js +++ b/eslint/lib/rules/no-unused-labels.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-unused-private-class-members.js b/eslint/lib/rules/no-unused-private-class-members.js index 74cf6ab..754c360 100644 --- a/eslint/lib/rules/no-unused-private-class-members.js +++ b/eslint/lib/rules/no-unused-private-class-members.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-unused-vars.js b/eslint/lib/rules/no-unused-vars.js index 847e21b..eaf7a8b 100644 --- a/eslint/lib/rules/no-unused-vars.js +++ b/eslint/lib/rules/no-unused-vars.js @@ -27,6 +27,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-use-before-define.js b/eslint/lib/rules/no-use-before-define.js index 7f904f4..07d035c 100644 --- a/eslint/lib/rules/no-use-before-define.js +++ b/eslint/lib/rules/no-use-before-define.js @@ -218,6 +218,7 @@ function isEvaluatedDuringInitialization(reference) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-useless-backreference.js b/eslint/lib/rules/no-useless-backreference.js index ae49147..1a09988 100644 --- a/eslint/lib/rules/no-useless-backreference.js +++ b/eslint/lib/rules/no-useless-backreference.js @@ -58,6 +58,7 @@ function isNegativeLookaround(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/no-useless-call.js b/eslint/lib/rules/no-useless-call.js index 8935066..8c57cd5 100644 --- a/eslint/lib/rules/no-useless-call.js +++ b/eslint/lib/rules/no-useless-call.js @@ -49,6 +49,7 @@ function isValidThisArg(expectedThis, thisArg, sourceCode) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-useless-catch.js b/eslint/lib/rules/no-useless-catch.js index 280ba55..325a2e5 100644 --- a/eslint/lib/rules/no-useless-catch.js +++ b/eslint/lib/rules/no-useless-catch.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-useless-computed-key.js b/eslint/lib/rules/no-useless-computed-key.js index a876921..7ebbe09 100644 --- a/eslint/lib/rules/no-useless-computed-key.js +++ b/eslint/lib/rules/no-useless-computed-key.js @@ -85,6 +85,7 @@ function hasUselessComputedKey(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-useless-concat.js b/eslint/lib/rules/no-useless-concat.js index a0176a7..36ca84f 100644 --- a/eslint/lib/rules/no-useless-concat.js +++ b/eslint/lib/rules/no-useless-concat.js @@ -64,6 +64,7 @@ function getRight(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-useless-constructor.js b/eslint/lib/rules/no-useless-constructor.js index 13ec675..6512c8b 100644 --- a/eslint/lib/rules/no-useless-constructor.js +++ b/eslint/lib/rules/no-useless-constructor.js @@ -132,6 +132,7 @@ function isRedundantSuperCall(body, ctorParams) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-useless-escape.js b/eslint/lib/rules/no-useless-escape.js index a780a7a..123bc5b 100644 --- a/eslint/lib/rules/no-useless-escape.js +++ b/eslint/lib/rules/no-useless-escape.js @@ -78,6 +78,7 @@ function parseRegExp(regExpText) { return charList; } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-useless-rename.js b/eslint/lib/rules/no-useless-rename.js index c0d27e6..616ec2a 100644 --- a/eslint/lib/rules/no-useless-rename.js +++ b/eslint/lib/rules/no-useless-rename.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-useless-return.js b/eslint/lib/rules/no-useless-return.js index 87f0589..0baa6b2 100644 --- a/eslint/lib/rules/no-useless-return.js +++ b/eslint/lib/rules/no-useless-return.js @@ -61,6 +61,7 @@ function isInFinally(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-var.js b/eslint/lib/rules/no-var.js index a821c38..83a1f62 100644 --- a/eslint/lib/rules/no-var.js +++ b/eslint/lib/rules/no-var.js @@ -179,6 +179,7 @@ function hasNameDisallowedForLetDeclarations(variable) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-void.js b/eslint/lib/rules/no-void.js index dba4932..8631caf 100644 --- a/eslint/lib/rules/no-void.js +++ b/eslint/lib/rules/no-void.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-warning-comments.js b/eslint/lib/rules/no-warning-comments.js index 23e3da3..5f3ea21 100644 --- a/eslint/lib/rules/no-warning-comments.js +++ b/eslint/lib/rules/no-warning-comments.js @@ -14,6 +14,7 @@ const CHAR_LIMIT = 40; // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/no-whitespace-before-property.js b/eslint/lib/rules/no-whitespace-before-property.js index 9a49299..95e920f 100644 --- a/eslint/lib/rules/no-whitespace-before-property.js +++ b/eslint/lib/rules/no-whitespace-before-property.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/no-with.js b/eslint/lib/rules/no-with.js index 219a680..fc93f19 100644 --- a/eslint/lib/rules/no-with.js +++ b/eslint/lib/rules/no-with.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/nonblock-statement-body-position.js b/eslint/lib/rules/nonblock-statement-body-position.js index 7ed541b..c177cf3 100644 --- a/eslint/lib/rules/nonblock-statement-body-position.js +++ b/eslint/lib/rules/nonblock-statement-body-position.js @@ -10,6 +10,7 @@ const POSITION_SCHEMA = { enum: ["beside", "below", "any"] }; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/object-curly-newline.js b/eslint/lib/rules/object-curly-newline.js index e160991..e052cd8 100644 --- a/eslint/lib/rules/object-curly-newline.js +++ b/eslint/lib/rules/object-curly-newline.js @@ -144,6 +144,7 @@ function areLineBreaksRequired(node, options, first, last) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/object-curly-spacing.js b/eslint/lib/rules/object-curly-spacing.js index b18ef57..9122da3 100644 --- a/eslint/lib/rules/object-curly-spacing.js +++ b/eslint/lib/rules/object-curly-spacing.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/object-property-newline.js b/eslint/lib/rules/object-property-newline.js index 7cca23f..dac084c 100644 --- a/eslint/lib/rules/object-property-newline.js +++ b/eslint/lib/rules/object-property-newline.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/object-shorthand.js b/eslint/lib/rules/object-shorthand.js index 10bb07b..aa03450 100644 --- a/eslint/lib/rules/object-shorthand.js +++ b/eslint/lib/rules/object-shorthand.js @@ -22,6 +22,7 @@ const astUtils = require("./utils/ast-utils"); //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/one-var-declaration-per-line.js b/eslint/lib/rules/one-var-declaration-per-line.js index c0ad700..440146b 100644 --- a/eslint/lib/rules/one-var-declaration-per-line.js +++ b/eslint/lib/rules/one-var-declaration-per-line.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/one-var.js b/eslint/lib/rules/one-var.js index daff2d2..1818c02 100644 --- a/eslint/lib/rules/one-var.js +++ b/eslint/lib/rules/one-var.js @@ -28,6 +28,7 @@ function isInStatementList(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/operator-assignment.js b/eslint/lib/rules/operator-assignment.js index 34bdb86..d200811 100644 --- a/eslint/lib/rules/operator-assignment.js +++ b/eslint/lib/rules/operator-assignment.js @@ -57,6 +57,7 @@ function canBeFixed(node) { ); } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/operator-linebreak.js b/eslint/lib/rules/operator-linebreak.js index 6eab0cc..a04f85b 100644 --- a/eslint/lib/rules/operator-linebreak.js +++ b/eslint/lib/rules/operator-linebreak.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/padded-blocks.js b/eslint/lib/rules/padded-blocks.js index de75e8d..336adac 100644 --- a/eslint/lib/rules/padded-blocks.js +++ b/eslint/lib/rules/padded-blocks.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/padding-line-between-statements.js b/eslint/lib/rules/padding-line-between-statements.js index 42859dd..fe5d304 100644 --- a/eslint/lib/rules/padding-line-between-statements.js +++ b/eslint/lib/rules/padding-line-between-statements.js @@ -425,6 +425,7 @@ const StatementTypes = { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/prefer-arrow-callback.js b/eslint/lib/rules/prefer-arrow-callback.js index 518bf4b..55a098a 100644 --- a/eslint/lib/rules/prefer-arrow-callback.js +++ b/eslint/lib/rules/prefer-arrow-callback.js @@ -145,6 +145,7 @@ function hasDuplicateParams(paramsList) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-const.js b/eslint/lib/rules/prefer-const.js index 38ec973..1908600 100644 --- a/eslint/lib/rules/prefer-const.js +++ b/eslint/lib/rules/prefer-const.js @@ -326,6 +326,7 @@ function findUp(node, type, shouldStop) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-destructuring.js b/eslint/lib/rules/prefer-destructuring.js index 46986d2..1f68313 100644 --- a/eslint/lib/rules/prefer-destructuring.js +++ b/eslint/lib/rules/prefer-destructuring.js @@ -20,6 +20,7 @@ const PRECEDENCE_OF_ASSIGNMENT_EXPR = astUtils.getPrecedence({ type: "Assignment // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-exponentiation-operator.js b/eslint/lib/rules/prefer-exponentiation-operator.js index de802ce..a291e8d 100644 --- a/eslint/lib/rules/prefer-exponentiation-operator.js +++ b/eslint/lib/rules/prefer-exponentiation-operator.js @@ -84,6 +84,7 @@ function parenthesizeIfShould(text, shouldParenthesize) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-named-capture-group.js b/eslint/lib/rules/prefer-named-capture-group.js index 41aa549..cff2d8f 100644 --- a/eslint/lib/rules/prefer-named-capture-group.js +++ b/eslint/lib/rules/prefer-named-capture-group.js @@ -27,6 +27,7 @@ const parser = new regexpp.RegExpParser(); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-numeric-literals.js b/eslint/lib/rules/prefer-numeric-literals.js index 91bb267..53a5153 100644 --- a/eslint/lib/rules/prefer-numeric-literals.js +++ b/eslint/lib/rules/prefer-numeric-literals.js @@ -39,6 +39,7 @@ function isParseInt(calleeNode) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-object-spread.js b/eslint/lib/rules/prefer-object-spread.js index 3958a51..b63474e 100644 --- a/eslint/lib/rules/prefer-object-spread.js +++ b/eslint/lib/rules/prefer-object-spread.js @@ -240,6 +240,7 @@ function defineFixer(node, sourceCode) { }; } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-promise-reject-errors.js b/eslint/lib/rules/prefer-promise-reject-errors.js index bdc1fef..60e72f4 100644 --- a/eslint/lib/rules/prefer-promise-reject-errors.js +++ b/eslint/lib/rules/prefer-promise-reject-errors.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-reflect.js b/eslint/lib/rules/prefer-reflect.js index fea88c6..1367a18 100644 --- a/eslint/lib/rules/prefer-reflect.js +++ b/eslint/lib/rules/prefer-reflect.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-regex-literals.js b/eslint/lib/rules/prefer-regex-literals.js index fbfeb56..afd6a01 100644 --- a/eslint/lib/rules/prefer-regex-literals.js +++ b/eslint/lib/rules/prefer-regex-literals.js @@ -48,6 +48,7 @@ function isStaticTemplateLiteral(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-rest-params.js b/eslint/lib/rules/prefer-rest-params.js index 157f0bb..371a289 100644 --- a/eslint/lib/rules/prefer-rest-params.js +++ b/eslint/lib/rules/prefer-rest-params.js @@ -59,6 +59,7 @@ function isNotNormalMemberAccess(reference) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-spread.js b/eslint/lib/rules/prefer-spread.js index 3944fed..c5f9e1e 100644 --- a/eslint/lib/rules/prefer-spread.js +++ b/eslint/lib/rules/prefer-spread.js @@ -43,6 +43,7 @@ function isValidThisArg(expectedThis, thisArg, context) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/prefer-template.js b/eslint/lib/rules/prefer-template.js index 564dd55..953155d 100644 --- a/eslint/lib/rules/prefer-template.js +++ b/eslint/lib/rules/prefer-template.js @@ -122,6 +122,7 @@ function endsWithTemplateCurly(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/quote-props.js b/eslint/lib/rules/quote-props.js index ce277cd..e57ad6e 100644 --- a/eslint/lib/rules/quote-props.js +++ b/eslint/lib/rules/quote-props.js @@ -16,6 +16,7 @@ const keywords = require("./utils/keywords"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/quotes.js b/eslint/lib/rules/quotes.js index d7959c0..a996096 100644 --- a/eslint/lib/rules/quotes.js +++ b/eslint/lib/rules/quotes.js @@ -74,6 +74,7 @@ const AVOID_ESCAPE = "avoid-escape"; // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/radix.js b/eslint/lib/rules/radix.js index 0c6c6ff..f83c762 100644 --- a/eslint/lib/rules/radix.js +++ b/eslint/lib/rules/radix.js @@ -74,6 +74,7 @@ function isDefaultRadix(radix) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/require-atomic-updates.js b/eslint/lib/rules/require-atomic-updates.js index 248b0eb..4dbd48d 100644 --- a/eslint/lib/rules/require-atomic-updates.js +++ b/eslint/lib/rules/require-atomic-updates.js @@ -165,6 +165,7 @@ class SegmentInfo { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/require-await.js b/eslint/lib/rules/require-await.js index 8ec6f54..1b17de0 100644 --- a/eslint/lib/rules/require-await.js +++ b/eslint/lib/rules/require-await.js @@ -28,6 +28,7 @@ function capitalizeFirstLetter(text) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/require-jsdoc.js b/eslint/lib/rules/require-jsdoc.js index 1d76e3d..169b6f5 100644 --- a/eslint/lib/rules/require-jsdoc.js +++ b/eslint/lib/rules/require-jsdoc.js @@ -5,6 +5,7 @@ */ "use strict"; +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/require-unicode-regexp.js b/eslint/lib/rules/require-unicode-regexp.js index a332b48..577ae6c 100644 --- a/eslint/lib/rules/require-unicode-regexp.js +++ b/eslint/lib/rules/require-unicode-regexp.js @@ -20,6 +20,7 @@ const { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/require-yield.js b/eslint/lib/rules/require-yield.js index f5b5d53..aba0614 100644 --- a/eslint/lib/rules/require-yield.js +++ b/eslint/lib/rules/require-yield.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/rest-spread-spacing.js b/eslint/lib/rules/rest-spread-spacing.js index a636def..ace1ec5 100644 --- a/eslint/lib/rules/rest-spread-spacing.js +++ b/eslint/lib/rules/rest-spread-spacing.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/semi-spacing.js b/eslint/lib/rules/semi-spacing.js index e5e2ae2..4f0afbb 100644 --- a/eslint/lib/rules/semi-spacing.js +++ b/eslint/lib/rules/semi-spacing.js @@ -11,6 +11,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/semi-style.js b/eslint/lib/rules/semi-style.js index 2d17d02..7952a9a 100644 --- a/eslint/lib/rules/semi-style.js +++ b/eslint/lib/rules/semi-style.js @@ -67,6 +67,7 @@ function isLastChild(node) { return nodeList !== null && nodeList[nodeList.length - 1] === node; // before `}` or etc. } +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/semi.js b/eslint/lib/rules/semi.js index c29029c..86ff8d7 100644 --- a/eslint/lib/rules/semi.js +++ b/eslint/lib/rules/semi.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/sort-imports.js b/eslint/lib/rules/sort-imports.js index dd43dad..13cb636 100644 --- a/eslint/lib/rules/sort-imports.js +++ b/eslint/lib/rules/sort-imports.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/sort-keys.js b/eslint/lib/rules/sort-keys.js index 65a9914..2fc1963 100644 --- a/eslint/lib/rules/sort-keys.js +++ b/eslint/lib/rules/sort-keys.js @@ -75,6 +75,7 @@ const isValidOrders = { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/sort-vars.js b/eslint/lib/rules/sort-vars.js index 0616c44..8246558 100644 --- a/eslint/lib/rules/sort-vars.js +++ b/eslint/lib/rules/sort-vars.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/space-before-blocks.js b/eslint/lib/rules/space-before-blocks.js index 87d5b27..5cc7266 100644 --- a/eslint/lib/rules/space-before-blocks.js +++ b/eslint/lib/rules/space-before-blocks.js @@ -34,6 +34,7 @@ function isFunctionBody(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/space-before-function-paren.js b/eslint/lib/rules/space-before-function-paren.js index b60bee0..fdd45be 100644 --- a/eslint/lib/rules/space-before-function-paren.js +++ b/eslint/lib/rules/space-before-function-paren.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/space-in-parens.js b/eslint/lib/rules/space-in-parens.js index 24378b8..1509d60 100644 --- a/eslint/lib/rules/space-in-parens.js +++ b/eslint/lib/rules/space-in-parens.js @@ -10,6 +10,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/space-infix-ops.js b/eslint/lib/rules/space-infix-ops.js index 8065b52..c526b7e 100644 --- a/eslint/lib/rules/space-infix-ops.js +++ b/eslint/lib/rules/space-infix-ops.js @@ -10,6 +10,7 @@ const { isEqToken } = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/space-unary-ops.js b/eslint/lib/rules/space-unary-ops.js index de9018f..04487c4 100644 --- a/eslint/lib/rules/space-unary-ops.js +++ b/eslint/lib/rules/space-unary-ops.js @@ -14,6 +14,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/spaced-comment.js b/eslint/lib/rules/spaced-comment.js index 6f0b432..674c13c 100644 --- a/eslint/lib/rules/spaced-comment.js +++ b/eslint/lib/rules/spaced-comment.js @@ -146,6 +146,7 @@ function createNeverStylePattern(markers) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/strict.js b/eslint/lib/rules/strict.js index 24af39d..0ea1da5 100644 --- a/eslint/lib/rules/strict.js +++ b/eslint/lib/rules/strict.js @@ -63,6 +63,7 @@ function isSimpleParameterList(params) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/switch-colon-spacing.js b/eslint/lib/rules/switch-colon-spacing.js index a4f3ba2..cd2ca70 100644 --- a/eslint/lib/rules/switch-colon-spacing.js +++ b/eslint/lib/rules/switch-colon-spacing.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/symbol-description.js b/eslint/lib/rules/symbol-description.js index 9f5d935..07bb8cd 100644 --- a/eslint/lib/rules/symbol-description.js +++ b/eslint/lib/rules/symbol-description.js @@ -16,6 +16,7 @@ const astUtils = require("./utils/ast-utils"); //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/template-curly-spacing.js b/eslint/lib/rules/template-curly-spacing.js index 5133a54..c842b76 100644 --- a/eslint/lib/rules/template-curly-spacing.js +++ b/eslint/lib/rules/template-curly-spacing.js @@ -15,6 +15,7 @@ const astUtils = require("./utils/ast-utils"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/template-tag-spacing.js b/eslint/lib/rules/template-tag-spacing.js index 45b6606..fa1a613 100644 --- a/eslint/lib/rules/template-tag-spacing.js +++ b/eslint/lib/rules/template-tag-spacing.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/unicode-bom.js b/eslint/lib/rules/unicode-bom.js index e80497d..d480f1b 100644 --- a/eslint/lib/rules/unicode-bom.js +++ b/eslint/lib/rules/unicode-bom.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/use-isnan.js b/eslint/lib/rules/use-isnan.js index cd8331f..9290350 100644 --- a/eslint/lib/rules/use-isnan.js +++ b/eslint/lib/rules/use-isnan.js @@ -31,6 +31,7 @@ function isNaNIdentifier(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/valid-jsdoc.js b/eslint/lib/rules/valid-jsdoc.js index 824410b..c118e03 100644 --- a/eslint/lib/rules/valid-jsdoc.js +++ b/eslint/lib/rules/valid-jsdoc.js @@ -15,6 +15,7 @@ const doctrine = require("doctrine"); // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/valid-typeof.js b/eslint/lib/rules/valid-typeof.js index 33b64f5..6046358 100644 --- a/eslint/lib/rules/valid-typeof.js +++ b/eslint/lib/rules/valid-typeof.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "problem", diff --git a/eslint/lib/rules/vars-on-top.js b/eslint/lib/rules/vars-on-top.js index 0f95d58..09e9932 100644 --- a/eslint/lib/rules/vars-on-top.js +++ b/eslint/lib/rules/vars-on-top.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/rules/wrap-iife.js b/eslint/lib/rules/wrap-iife.js index 498d7bd..8523796 100644 --- a/eslint/lib/rules/wrap-iife.js +++ b/eslint/lib/rules/wrap-iife.js @@ -37,6 +37,7 @@ function isCalleeOfNewExpression(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/wrap-regex.js b/eslint/lib/rules/wrap-regex.js index 945eb5e..b10f2ec 100644 --- a/eslint/lib/rules/wrap-regex.js +++ b/eslint/lib/rules/wrap-regex.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/yield-star-spacing.js b/eslint/lib/rules/yield-star-spacing.js index 8c3eefa..884a3a4 100644 --- a/eslint/lib/rules/yield-star-spacing.js +++ b/eslint/lib/rules/yield-star-spacing.js @@ -9,6 +9,7 @@ // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "layout", diff --git a/eslint/lib/rules/yoda.js b/eslint/lib/rules/yoda.js index 2d018dc..f491f75 100644 --- a/eslint/lib/rules/yoda.js +++ b/eslint/lib/rules/yoda.js @@ -115,6 +115,7 @@ function getNormalizedLiteral(node) { // Rule Definition //------------------------------------------------------------------------------ +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", diff --git a/eslint/lib/shared/types.js b/eslint/lib/shared/types.js index c497f78..ab59207 100644 --- a/eslint/lib/shared/types.js +++ b/eslint/lib/shared/types.js @@ -23,6 +23,16 @@ module.exports = {}; * @property {EcmaFeatures} [ecmaFeatures] The optional features. * @property {3|5|6|7|8|9|10|11|12|13|2015|2016|2017|2018|2019|2020|2021|2022} [ecmaVersion] The ECMAScript version (or revision number). * @property {"script"|"module"} [sourceType] The source code type. + * @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3. + */ + +/** + * @typedef {Object} LanguageOptions + * @property {number|"latest"} [ecmaVersion] The ECMAScript version (or revision number). + * @property {Record} [globals] The global variable settings. + * @property {"script"|"module"|"commonjs"} [sourceType] The source code type. + * @property {string|Object} [parser] The parser to use. + * @property {Object} [parserOptions] The parser options to use. */ /** diff --git a/eslint/package.json b/eslint/package.json index c3dfa1b..82cc50e 100644 --- a/eslint/package.json +++ b/eslint/package.json @@ -1,6 +1,6 @@ { "name": "eslint", - "version": "8.3.0", + "version": "8.4.0", "author": "Nicholas C. Zakas ", "description": "An AST-based pattern checker for JavaScript.", "bin": { @@ -47,8 +47,8 @@ "homepage": "https://eslint.org", "bugs": "https://github.com/eslint/eslint/issues/", "dependencies": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -59,7 +59,7 @@ "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.1.0", - "espree": "^9.1.0", + "espree": "^9.2.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -116,8 +116,8 @@ "karma-webpack": "^5.0.0", "lint-staged": "^11.0.0", "load-perf": "^0.2.0", - "markdownlint": "^0.23.1", - "markdownlint-cli": "^0.28.1", + "markdownlint": "^0.24.0", + "markdownlint-cli": "^0.30.0", "memfs": "^3.0.1", "mocha": "^8.3.2", "mocha-junit-reporter": "^2.0.0", diff --git a/eslint/templates/blogpost.md.ejs b/eslint/templates/blogpost.md.ejs index 71e1973..37be7ec 100644 --- a/eslint/templates/blogpost.md.ejs +++ b/eslint/templates/blogpost.md.ejs @@ -1,13 +1,14 @@ --- layout: post title: ESLint v<%- version %> released +teaser: "We just pushed ESLint v<%- version %>, which is a <%- type %> release upgrade of ESLint. This release <% if (type !== "patch") { %>adds some new features and <% } %>fixes several bugs found in the previous release.<% if (type === "major") { %> This release also has some breaking changes, so please read the following closely.<% } %>" +categories: + - Release Notes tags: - release - <%- type %> --- -We just pushed ESLint v<%- version %>, which is a <%- type %> release upgrade of ESLint. This release <% if (type !== "patch") { %>adds some new features and <% } %>fixes several bugs found in the previous release.<% if (type === "major") { %> This release also has some breaking changes, so please read the following closely.<% } %> - <% const RULE_REGEX = new RegExp(`\`?\\b(${ruleList.join("|")})\\b\`?`, "g"); diff --git a/eslint/templates/bug-report.md b/eslint/templates/bug-report.md index bd3035b..c8c552d 100644 --- a/eslint/templates/bug-report.md +++ b/eslint/templates/bug-report.md @@ -1,4 +1,4 @@ -**Tell us about your environment** +**Tell us about your environment:** * **ESLint Version:** * **Node Version:** @@ -23,4 +23,3 @@ **What did you expect to happen?** **What actually happened? Please include the actual, raw output from ESLint.** - diff --git a/eslint/templates/rule-change-proposal.md b/eslint/templates/rule-change-proposal.md index ee10001..a4a8e17 100644 --- a/eslint/templates/rule-change-proposal.md +++ b/eslint/templates/rule-change-proposal.md @@ -1,12 +1,9 @@ **What rule do you want to change?** - **Does this change cause the rule to produce more or fewer warnings?** - **How will the change be implemented? (New option, new default behavior, etc.)?** - **Please provide some example code that this change will affect:** ```js @@ -15,6 +12,4 @@ **What does the rule currently do for this code?** - **What will the rule do after it's changed?** - diff --git a/eslint/templates/rule-proposal.md b/eslint/templates/rule-proposal.md index 383b1b4..2adf931 100644 --- a/eslint/templates/rule-proposal.md +++ b/eslint/templates/rule-proposal.md @@ -1,7 +1,6 @@ **Please describe what the rule should do:** - -**What category of rule is this? (place an "X" next to just one item)** +**What category of rule is this (place an "X" next to just one item)?** [ ] Enforces code style [ ] Warns about a potential error @@ -15,4 +14,3 @@ ``` **Why should this rule be included in ESLint (instead of a plugin)?** - diff --git a/eslint/tests/fixtures/formatters/async.js b/eslint/tests/fixtures/formatters/async.js new file mode 100644 index 0000000..b5651a6 --- /dev/null +++ b/eslint/tests/fixtures/formatters/async.js @@ -0,0 +1,4 @@ +/*global module*/ +module.exports = function(results) { + return Promise.resolve('from async formatter'); +}; diff --git a/eslint/tests/fixtures/formatters/cwd.js b/eslint/tests/fixtures/formatters/cwd.js new file mode 100644 index 0000000..b9c4966 --- /dev/null +++ b/eslint/tests/fixtures/formatters/cwd.js @@ -0,0 +1,4 @@ +/*global module*/ +module.exports = function(results, context) { + return context.cwd; +}; diff --git a/eslint/tests/fixtures/parsers/throws-with-options.js b/eslint/tests/fixtures/parsers/throws-with-options.js index 9585775..0c701ed 100644 --- a/eslint/tests/fixtures/parsers/throws-with-options.js +++ b/eslint/tests/fixtures/parsers/throws-with-options.js @@ -4,7 +4,7 @@ const espree = require("espree"); exports.parse = (sourceText, options) => { if (options.ecmaVersion) { - throw new Error("Expected no parserOptions to be used"); + throw new Error("Expected no parserOptions.ecmaVersion to be used"); } return espree.parse(sourceText, options); }; diff --git a/eslint/tests/lib/cli-engine/cli-engine.js b/eslint/tests/lib/cli-engine/cli-engine.js index 31f59bf..2ba4839 100644 --- a/eslint/tests/lib/cli-engine/cli-engine.js +++ b/eslint/tests/lib/cli-engine/cli-engine.js @@ -1158,28 +1158,44 @@ describe("CLIEngine", () => { configFile: getFixturePath("configurations", "semi-error.json") }); - const report = engine.executeOnFiles([getFixturePath("formatters")]); + const fixturePath = getFixturePath("formatters"); + const report = engine.executeOnFiles([fixturePath]); - assert.strictEqual(report.results.length, 3); assert.strictEqual(report.errorCount, 0); assert.strictEqual(report.warningCount, 0); assert.strictEqual(report.fixableErrorCount, 0); assert.strictEqual(report.fixableWarningCount, 0); - assert.strictEqual(report.results[0].messages.length, 0); - assert.strictEqual(report.results[1].messages.length, 0); - assert.strictEqual(report.results[2].messages.length, 0); + assert.strictEqual(report.results.length, 5); + assert.strictEqual(path.relative(fixturePath, report.results[0].filePath), "async.js"); assert.strictEqual(report.results[0].errorCount, 0); assert.strictEqual(report.results[0].warningCount, 0); assert.strictEqual(report.results[0].fixableErrorCount, 0); assert.strictEqual(report.results[0].fixableWarningCount, 0); + assert.strictEqual(report.results[0].messages.length, 0); + assert.strictEqual(path.relative(fixturePath, report.results[1].filePath), "broken.js"); assert.strictEqual(report.results[1].errorCount, 0); assert.strictEqual(report.results[1].warningCount, 0); assert.strictEqual(report.results[1].fixableErrorCount, 0); assert.strictEqual(report.results[1].fixableWarningCount, 0); + assert.strictEqual(report.results[1].messages.length, 0); + assert.strictEqual(path.relative(fixturePath, report.results[2].filePath), "cwd.js"); assert.strictEqual(report.results[2].errorCount, 0); assert.strictEqual(report.results[2].warningCount, 0); assert.strictEqual(report.results[2].fixableErrorCount, 0); assert.strictEqual(report.results[2].fixableWarningCount, 0); + assert.strictEqual(report.results[2].messages.length, 0); + assert.strictEqual(path.relative(fixturePath, report.results[3].filePath), "simple.js"); + assert.strictEqual(report.results[3].errorCount, 0); + assert.strictEqual(report.results[3].warningCount, 0); + assert.strictEqual(report.results[3].fixableErrorCount, 0); + assert.strictEqual(report.results[3].fixableWarningCount, 0); + assert.strictEqual(report.results[3].messages.length, 0); + assert.strictEqual(path.relative(fixturePath, report.results[4].filePath), path.join("test", "simple.js")); + assert.strictEqual(report.results[4].errorCount, 0); + assert.strictEqual(report.results[4].warningCount, 0); + assert.strictEqual(report.results[4].fixableErrorCount, 0); + assert.strictEqual(report.results[4].fixableWarningCount, 0); + assert.strictEqual(report.results[4].messages.length, 0); }); @@ -1190,24 +1206,39 @@ describe("CLIEngine", () => { configFile: getFixturePath("configurations", "single-quotes-error.json") }); - const report = engine.executeOnFiles([getFixturePath("formatters")]); + const fixturePath = getFixturePath("formatters"); + const report = engine.executeOnFiles([fixturePath]); assert.strictEqual(report.errorCount, 6); assert.strictEqual(report.warningCount, 0); assert.strictEqual(report.fixableErrorCount, 6); assert.strictEqual(report.fixableWarningCount, 0); + assert.strictEqual(report.results.length, 5); + assert.strictEqual(path.relative(fixturePath, report.results[0].filePath), "async.js"); assert.strictEqual(report.results[0].errorCount, 0); assert.strictEqual(report.results[0].warningCount, 0); assert.strictEqual(report.results[0].fixableErrorCount, 0); assert.strictEqual(report.results[0].fixableWarningCount, 0); - assert.strictEqual(report.results[1].errorCount, 3); + assert.strictEqual(path.relative(fixturePath, report.results[1].filePath), "broken.js"); + assert.strictEqual(report.results[1].errorCount, 0); assert.strictEqual(report.results[1].warningCount, 0); - assert.strictEqual(report.results[1].fixableErrorCount, 3); + assert.strictEqual(report.results[1].fixableErrorCount, 0); assert.strictEqual(report.results[1].fixableWarningCount, 0); - assert.strictEqual(report.results[2].errorCount, 3); + assert.strictEqual(path.relative(fixturePath, report.results[2].filePath), "cwd.js"); + assert.strictEqual(report.results[2].errorCount, 0); assert.strictEqual(report.results[2].warningCount, 0); - assert.strictEqual(report.results[2].fixableErrorCount, 3); + assert.strictEqual(report.results[2].fixableErrorCount, 0); assert.strictEqual(report.results[2].fixableWarningCount, 0); + assert.strictEqual(path.relative(fixturePath, report.results[3].filePath), "simple.js"); + assert.strictEqual(report.results[3].errorCount, 3); + assert.strictEqual(report.results[3].warningCount, 0); + assert.strictEqual(report.results[3].fixableErrorCount, 3); + assert.strictEqual(report.results[3].fixableWarningCount, 0); + assert.strictEqual(path.relative(fixturePath, report.results[4].filePath), path.join("test", "simple.js")); + assert.strictEqual(report.results[4].errorCount, 3); + assert.strictEqual(report.results[4].warningCount, 0); + assert.strictEqual(report.results[4].fixableErrorCount, 3); + assert.strictEqual(report.results[4].fixableWarningCount, 0); }); it("should process when file is given by not specifying extensions", () => { diff --git a/eslint/tests/lib/cli.js b/eslint/tests/lib/cli.js index 1b3828b..59ff0ea 100644 --- a/eslint/tests/lib/cli.js +++ b/eslint/tests/lib/cli.js @@ -249,10 +249,13 @@ describe("cli", () => { // Check metadata. const { metadata } = JSON.parse(log.info.args[0][0]); - const expectedMetadata = Array.from(BuiltinRules).reduce((obj, [ruleId, rule]) => { - obj.rulesMeta[ruleId] = rule.meta; - return obj; - }, { rulesMeta: {} }); + const expectedMetadata = { + cwd: process.cwd(), + rulesMeta: Array.from(BuiltinRules).reduce((obj, [ruleId, rule]) => { + obj[ruleId] = rule.meta; + return obj; + }, {}) + }; assert.deepStrictEqual(metadata, expectedMetadata); }); @@ -287,6 +290,17 @@ describe("cli", () => { }); }); + describe("when given an async formatter path", () => { + it("should execute without any errors", async () => { + const formatterPath = getFixturePath("formatters", "async.js"); + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); + + assert.strictEqual(log.info.getCall(0).args[0], "from async formatter"); + assert.strictEqual(exit, 0); + }); + }); + describe("when executing a file with a lint error", () => { it("should exit with error", async () => { const filePath = getFixturePath("undef.js"); diff --git a/eslint/tests/lib/config/flat-config-array.js b/eslint/tests/lib/config/flat-config-array.js index f6b0990..64cad94 100644 --- a/eslint/tests/lib/config/flat-config-array.js +++ b/eslint/tests/lib/config/flat-config-array.js @@ -78,7 +78,7 @@ const baseConfig = { function createFlatConfigArray(configs) { return new FlatConfigArray(configs, { basePath: __dirname, - baseConfig + baseConfig: [baseConfig] }); } @@ -143,6 +143,29 @@ function normalizeRuleConfig(rulesConfig) { describe("FlatConfigArray", () => { + it("should not reuse languageOptions.parserOptions across configs", () => { + const base = [{ + languageOptions: { + parserOptions: { + foo: true + } + } + }]; + + const configs = new FlatConfigArray([], { + basePath: __dirname, + baseConfig: base + }); + + configs.normalizeSync(); + + const config = configs.getConfig("foo.js"); + + assert.notStrictEqual(base[0].languageOptions, config.languageOptions); + assert.notStrictEqual(base[0].languageOptions.parserOptions, config.languageOptions.parserOptions, "parserOptions should be new object"); + }); + + describe("Special configs", () => { it("eslint:recommended is replaced with an actual config", async () => { const configs = new FlatConfigArray(["eslint:recommended"], { basePath: __dirname }); @@ -638,7 +661,7 @@ describe("FlatConfigArray", () => { ecmaVersion: "true" } } - ], "Expected a number."); + ], /Key "languageOptions": Key "ecmaVersion": Expected a number or "latest"\./u); }); it("should merge two objects when second object has overrides", () => assertMergedResult([ diff --git a/eslint/tests/lib/eslint/eslint.js b/eslint/tests/lib/eslint/eslint.js index eaf4aaa..4e140f7 100644 --- a/eslint/tests/lib/eslint/eslint.js +++ b/eslint/tests/lib/eslint/eslint.js @@ -1210,24 +1210,40 @@ describe("ESLint", () => { cwd: path.join(fixtureDir, ".."), overrideConfigFile: getFixturePath("configurations", "semi-error.json") }); - const results = await eslint.lintFiles([getFixturePath("formatters")]); + const fixturePath = getFixturePath("formatters"); + const results = await eslint.lintFiles([fixturePath]); - assert.strictEqual(results.length, 3); - assert.strictEqual(results[0].messages.length, 0); - assert.strictEqual(results[1].messages.length, 0); - assert.strictEqual(results[2].messages.length, 0); + assert.strictEqual(results.length, 5); + assert.strictEqual(path.relative(fixturePath, results[0].filePath), "async.js"); assert.strictEqual(results[0].errorCount, 0); assert.strictEqual(results[0].warningCount, 0); assert.strictEqual(results[0].fixableErrorCount, 0); assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(path.relative(fixturePath, results[1].filePath), "broken.js"); assert.strictEqual(results[1].errorCount, 0); assert.strictEqual(results[1].warningCount, 0); assert.strictEqual(results[1].fixableErrorCount, 0); assert.strictEqual(results[1].fixableWarningCount, 0); + assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(path.relative(fixturePath, results[2].filePath), "cwd.js"); assert.strictEqual(results[2].errorCount, 0); assert.strictEqual(results[2].warningCount, 0); assert.strictEqual(results[2].fixableErrorCount, 0); assert.strictEqual(results[2].fixableWarningCount, 0); + assert.strictEqual(results[2].messages.length, 0); + assert.strictEqual(path.relative(fixturePath, results[3].filePath), "simple.js"); + assert.strictEqual(results[3].errorCount, 0); + assert.strictEqual(results[3].warningCount, 0); + assert.strictEqual(results[3].fixableErrorCount, 0); + assert.strictEqual(results[3].fixableWarningCount, 0); + assert.strictEqual(results[3].messages.length, 0); + assert.strictEqual(path.relative(fixturePath, results[4].filePath), path.join("test", "simple.js")); + assert.strictEqual(results[4].errorCount, 0); + assert.strictEqual(results[4].warningCount, 0); + assert.strictEqual(results[4].fixableErrorCount, 0); + assert.strictEqual(results[4].fixableWarningCount, 0); + assert.strictEqual(results[4].messages.length, 0); }); it("should process when file is given by not specifying extensions", async () => { @@ -4731,6 +4747,15 @@ describe("ESLint", () => { await engine.loadFormatter(5); }, /'name' must be a string/u); }); + + it("should pass cwd to the `cwd` property of the second argument.", async () => { + const cwd = getFixturePath(); + const engine = new ESLint({ cwd }); + const formatterPath = getFixturePath("formatters", "cwd.js"); + const formatter = await engine.loadFormatter(formatterPath); + + assert.strictEqual(formatter.format([]), cwd); + }); }); describe("getErrorResults()", () => { diff --git a/eslint/tests/lib/linter/linter.js b/eslint/tests/lib/linter/linter.js index 24dc4de..3343461 100644 --- a/eslint/tests/lib/linter/linter.js +++ b/eslint/tests/lib/linter/linter.js @@ -16,6 +16,7 @@ const assert = require("chai").assert, testParsers = require("../../fixtures/parsers/linter-test-parsers"); const { Linter } = require("../../../lib/linter"); +const { FlatConfigArray } = require("../../../lib/config/flat-config-array"); //------------------------------------------------------------------------------ // Constants @@ -95,8 +96,8 @@ describe("Linter", () => { linter.defineRule("checker", () => ({ Program: spy })); linter.verify("foo", { rules: { checker: "error" } }); - assert(spy.calledOnce); - assert.strictEqual(spy.firstCall.thisValue, void 0); + assert(spy.calledOnce, "Rule should have been called"); + assert.strictEqual(spy.firstCall.thisValue, void 0, "this value should be undefined"); }); it("does not allow listeners to use special EventEmitter values", () => { @@ -2356,7 +2357,7 @@ describe("Linter", () => { assert.strictEqual(messages[1].ruleId, "no-console"); }); - it("should not ignore violations if comment is of the type Shebang", () => { + it("should not ignore violations if comment is of the type hashbang", () => { const code = [ "#! eslint-disable-next-line no-alert", "alert('test');", @@ -2771,10 +2772,10 @@ var a = "test2"; }); }); - describe("when evaluating a file with a shebang", () => { - const code = "#!bin/program\n\nvar foo;;"; + describe("when evaluating a file with a hashbang", () => { it("should preserve line numbers", () => { + const code = "#!bin/program\n\nvar foo;;"; const config = { rules: { "no-extra-semi": 1 } }; const messages = linter.verify(code, config); @@ -2784,7 +2785,8 @@ var a = "test2"; assert.strictEqual(messages[0].line, 3); }); - it("should have a comment with the shebang in it", () => { + it("should have a comment with the hashbang in it", () => { + const code = "#!bin/program\n\nvar foo;;"; const config = { rules: { checker: "error" } }; const spy = sinon.spy(context => { const comments = context.getAllComments(); @@ -2798,6 +2800,23 @@ var a = "test2"; linter.verify(code, config); assert(spy.calledOnce); }); + + it("should comment hashbang without breaking offset", () => { + const code = "#!/usr/bin/env node\n'123';"; + const config = { rules: { checker: "error" } }; + let spy; + + linter.defineRule("checker", context => { + spy = sinon.spy(node => { + assert.strictEqual(context.getSource(node), "'123';"); + }); + return { ExpressionStatement: spy }; + }); + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); describe("when evaluating broken code", () => { @@ -2835,12 +2854,16 @@ var a = "test2"; linter = new Linter(); const code = TEST_CODE; - const results = linter.verify(code, { rules: { foobar: 2 } }); - const result = results[0]; - const warningResult = linter.verify(code, { rules: { foobar: 1 } })[0]; - const arrayOptionResults = linter.verify(code, { rules: { foobar: [2, "always"] } }); - const objectOptionResults = linter.verify(code, { rules: { foobar: [1, { bar: false }] } }); - const resultsMultiple = linter.verify(code, { rules: { foobar: 2, barfoo: 1 } }); + let results, result, warningResult, arrayOptionResults, objectOptionResults, resultsMultiple; + + beforeEach(() => { + results = linter.verify(code, { rules: { foobar: 2 } }); + result = results[0]; + warningResult = linter.verify(code, { rules: { foobar: 1 } })[0]; + arrayOptionResults = linter.verify(code, { rules: { foobar: [2, "always"] } }); + objectOptionResults = linter.verify(code, { rules: { foobar: [1, { bar: false }] } }); + resultsMultiple = linter.verify(code, { rules: { foobar: 2, barfoo: 1 } }); + }); it("should report a problem", () => { assert.isNotNull(result); @@ -2887,9 +2910,10 @@ var a = "test2"; describe("when using a rule which has been replaced", () => { const code = TEST_CODE; - const results = linter.verify(code, { rules: { "no-comma-dangle": 2 } }); it("should report the new rule", () => { + const results = linter.verify(code, { rules: { "no-comma-dangle": 2 } }); + assert.strictEqual(results[0].ruleId, "no-comma-dangle"); assert.strictEqual(results[0].message, "Rule 'no-comma-dangle' was removed and replaced by: comma-dangle"); }); @@ -4116,7 +4140,7 @@ var a = "test2"; assert.strictEqual(messages.length, 0); }); - it("the 'latest' is equal to espree.lastEcmaVersion", () => { + it("the 'latest' is equal to espree.latestEcmaVersion", () => { let ecmaVersion = null; const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } }; @@ -4126,7 +4150,62 @@ var a = "test2"; } })); linter.verify("", config); - assert.strictEqual(ecmaVersion, espree.latestEcmaVersion); + assert.strictEqual(ecmaVersion, espree.latestEcmaVersion, "ecmaVersion should be 13"); + }); + + it("the 'latest' is not normalized for custom parsers", () => { + let ecmaVersion = null; + const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "latest" } }; + + linter.defineParser("custom-parser", testParsers.enhancedParser); + linter.defineRule("ecma-version", context => ({ + Program() { + ecmaVersion = context.parserOptions.ecmaVersion; + } + })); + linter.verify("", config); + assert.strictEqual(ecmaVersion, "latest", "ecmaVersion should be latest"); + }); + + it("the 'latest' is equal to espree.latestEcmaVersion on languageOptions", () => { + let ecmaVersion = null; + const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } }; + + linter.defineRule("ecma-version", context => ({ + Program() { + ecmaVersion = context.languageOptions.ecmaVersion; + } + })); + linter.verify("", config); + assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022"); + }); + + it("the 'next' is equal to espree.latestEcmaVersion on languageOptions with custom parser", () => { + let ecmaVersion = null; + const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "next" } }; + + linter.defineParser("custom-parser", testParsers.stubParser); + linter.defineRule("ecma-version", context => ({ + Program() { + ecmaVersion = context.languageOptions.ecmaVersion; + } + })); + linter.verify("", config); + assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022"); + }); + + it("missing ecmaVersion is equal to 5 on languageOptions with custom parser", () => { + let ecmaVersion = null; + const config = { rules: { "ecma-version": 2 }, parser: "custom-parser" }; + + linter.defineParser("custom-parser", testParsers.enhancedParser); + linter.defineRule("ecma-version", context => ({ + Program() { + ecmaVersion = context.languageOptions.ecmaVersion; + } + })); + linter.verify("", config); + assert.strictEqual(ecmaVersion, 5, "ecmaVersion should be 5"); }); it("should pass normalized ecmaVersion to eslint-scope", () => { @@ -4358,6 +4437,82 @@ var a = "test2"; assert.strictEqual(messages.length, 0); }); + it("should not allow the use of reserved words as variable names in ES3", () => { + const code = "var char;"; + const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.isTrue(messages[0].fatal); + assert.match(messages[0].message, /^Parsing error:.*'char'/u); + }); + + it("should not allow the use of reserved words as property names in member expressions in ES3", () => { + const code = "obj.char;"; + const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.isTrue(messages[0].fatal); + assert.match(messages[0].message, /^Parsing error:.*'char'/u); + }); + + it("should not allow the use of reserved words as property names in object literals in ES3", () => { + const code = "var obj = { char: 1 };"; + const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.isTrue(messages[0].fatal); + assert.match(messages[0].message, /^Parsing error:.*'char'/u); + }); + + it("should allow the use of reserved words as variable and property names in ES3 when allowReserved is true", () => { + const code = "var char; obj.char; var obj = { char: 1 };"; + const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3, allowReserved: true } }, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not allow the use of reserved words as variable names in ES > 3", () => { + const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)]; + + ecmaVersions.forEach(ecmaVersion => { + const code = "var enum;"; + const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.isTrue(messages[0].fatal); + assert.match(messages[0].message, /^Parsing error:.*'enum'/u); + }); + }); + + it("should allow the use of reserved words as property names in ES > 3", () => { + const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)]; + + ecmaVersions.forEach(ecmaVersion => { + const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };"; + const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename); + + assert.strictEqual(messages.length, 0); + }); + }); + + it("should not allow `allowReserved: true` in ES > 3", () => { + const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)]; + + ecmaVersions.forEach(ecmaVersion => { + const code = ""; + const messages = linter.verify(code, { parserOptions: { ecmaVersion, allowReserved: true } }, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.isTrue(messages[0].fatal); + assert.match(messages[0].message, /^Parsing error:.*allowReserved/u); + }); + }); + it("should be able to use es6 features if there is a comment which has \"eslint-env es6\"", () => { const code = [ "/* eslint-env es6 */", @@ -6242,3 +6397,7264 @@ var a = "test2"; }); }); }); + +describe("Linter with FlatConfigArray", () => { + + let linter; + const filename = "filename.js"; + + /** + * Creates a config array with some default properties. + * @param {FlatConfig|FlatConfig[]} value The value to base the + * config array on. + * @returns {FlatConfigArray} The created config array. + */ + function createFlatConfigArray(value) { + return new FlatConfigArray(value, { basePath: "" }); + } + + beforeEach(() => { + linter = new Linter({ configType: "flat" }); + }); + + describe("Static Members", () => { + describe("version", () => { + it("should return same version as instance property", () => { + assert.strictEqual(Linter.version, linter.version); + }); + }); + }); + + describe("Config Options", () => { + + describe("languageOptions", () => { + + describe("ecmaVersion", () => { + + it("should error when accessing a global that isn't available in ecmaVersion 5", () => { + const messages = linter.verify("new Map()", { + languageOptions: { + ecmaVersion: 5, + sourceType: "script" + }, + rules: { + "no-undef": "error" + } + }); + + assert.strictEqual(messages.length, 1, "There should be one linting error."); + assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef."); + }); + + it("should error when accessing a global that isn't available in ecmaVersion 3", () => { + const messages = linter.verify("JSON.stringify({})", { + languageOptions: { + ecmaVersion: 3, + sourceType: "script" + }, + rules: { + "no-undef": "error" + } + }); + + assert.strictEqual(messages.length, 1, "There should be one linting error."); + assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef."); + }); + + it("should add globals for ES6 when ecmaVersion is 6", () => { + const messages = linter.verify("new Map()", { + languageOptions: { + ecmaVersion: 6 + }, + rules: { + "no-undef": "error" + } + }); + + assert.strictEqual(messages.length, 0, "There should be no linting errors."); + }); + + it("should allow destructuring when ecmaVersion is 6", () => { + const messages = linter.verify("let {a} = b", { + languageOptions: { + ecmaVersion: 6 + } + }); + + assert.strictEqual(messages.length, 0, "There should be no linting errors."); + }); + + it("ecmaVersion should be normalized to year name for ES 6", () => { + const config = { + plugins: { + test: { + rules: { + checker(context) { + return { + Program() { + assert.strictEqual(context.languageOptions.ecmaVersion, 2015); + } + }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + linter.verify("foo", config, filename); + }); + + it("ecmaVersion should be normalized to latest year by default", () => { + const config = { + plugins: { + test: { + rules: { + checker(context) { + return { + Program() { + assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009); + } + }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify("foo", config, filename); + }); + + it("ecmaVersion should not be normalized to year name for ES 5", () => { + const config = { + plugins: { + test: { + rules: { + checker(context) { + return { + Program() { + assert.strictEqual(context.languageOptions.ecmaVersion, 5); + } + }; + } + } + } + }, + languageOptions: { + ecmaVersion: 5 + }, + rules: { "test/checker": "error" } + }; + + linter.verify("foo", config, filename); + }); + + it("ecmaVersion should be normalized to year name for 'latest'", () => { + const config = { + plugins: { + test: { + rules: { + checker(context) { + return { + Program() { + assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009); + } + }; + } + } + } + }, + languageOptions: { + ecmaVersion: "latest" + }, + rules: { "test/checker": "error" } + }; + + linter.verify("foo", config, filename); + }); + + + }); + + describe("sourceType", () => { + + it("should be module by default", () => { + const config = { + plugins: { + test: { + rules: { + checker(context) { + return { + Program() { + assert.strictEqual(context.languageOptions.sourceType, "module"); + } + }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify("import foo from 'bar'", config, filename); + }); + + it("should default to commonjs when passed a .cjs filename", () => { + const config = { + plugins: { + test: { + rules: { + checker(context) { + return { + Program() { + assert.strictEqual(context.languageOptions.sourceType, "commonjs"); + } + }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify("import foo from 'bar'", config, `${filename}.cjs`); + }); + + + it("should error when import is used in a script", () => { + const messages = linter.verify("import foo from 'bar';", { + languageOptions: { + ecmaVersion: 6, + sourceType: "script" + } + }); + + assert.strictEqual(messages.length, 1, "There should be one parsing error."); + assert.strictEqual(messages[0].message, "Parsing error: 'import' and 'export' may appear only with 'sourceType: module'"); + }); + + it("should not error when import is used in a module", () => { + const messages = linter.verify("import foo from 'bar';", { + languageOptions: { + ecmaVersion: 6, + sourceType: "module" + } + }); + + assert.strictEqual(messages.length, 0, "There should no linting errors."); + }); + + it("should error when return is used at the top-level outside of commonjs", () => { + const messages = linter.verify("return", { + languageOptions: { + ecmaVersion: 6, + sourceType: "script" + } + }); + + assert.strictEqual(messages.length, 1, "There should be one parsing error."); + assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function"); + }); + + it("should not error when top-level return is used in commonjs", () => { + const messages = linter.verify("return", { + languageOptions: { + ecmaVersion: 6, + sourceType: "commonjs" + } + }); + + assert.strictEqual(messages.length, 0, "There should no linting errors."); + }); + + it("should error when accessing a Node.js global outside of commonjs", () => { + const messages = linter.verify("require()", { + languageOptions: { + ecmaVersion: 6 + }, + rules: { + "no-undef": "error" + } + }); + + assert.strictEqual(messages.length, 1, "There should be one linting error."); + assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef."); + }); + + it("should add globals for Node.js when sourceType is commonjs", () => { + const messages = linter.verify("require()", { + languageOptions: { + ecmaVersion: 6, + sourceType: "commonjs" + }, + rules: { + "no-undef": "error" + } + }); + + assert.strictEqual(messages.length, 0, "There should be no linting errors."); + }); + + it("should allow 'await' as a property name in modules", () => { + const result = linter.verify( + "obj.await", + { + languageOptions: { + ecmaVersion: 6, + sourceType: "module" + } + } + ); + + assert(result.length === 0); + }); + + }); + + describe("parser", () => { + + it("should be able to define a custom parser", () => { + const parser = { + parseForESLint: function parse(code, options) { + return { + ast: esprima.parse(code, options), + services: { + test: { + getMessage() { + return "Hi!"; + } + } + } + }; + } + }; + + const config = { + plugins: { + test: { + parsers: { + "test-parser": parser + } + } + }, + languageOptions: { + parser: "test/test-parser" + } + }; + + + const messages = linter.verify("0", config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should pass parser as context.languageOptions.parser to all rules when provided on config", () => { + + const config = { + plugins: { + test: { + rules: { + "test-rule": sinon.mock().withArgs( + sinon.match({ languageOptions: { parser: esprima } }) + ).returns({}) + } + } + }, + languageOptions: { + parser: esprima + }, + rules: { + "test/test-rule": 2 + } + }; + + linter.verify("0", config, filename); + }); + + it("should use parseForESLint() in custom parser when custom parser is specified", () => { + const config = { + plugins: { + test: { + parsers: { + "enhanced-parser": testParsers.enhancedParser + } + } + }, + languageOptions: { + parser: "test/enhanced-parser" + } + }; + + const messages = linter.verify("0", config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should expose parser services when using parseForESLint() and services are specified", () => { + + const config = { + plugins: { + test: { + parsers: { + "enhanced-parser": testParsers.enhancedParser + }, + rules: { + "test-service-rule": context => ({ + Literal(node) { + context.report({ + node, + message: context.parserServices.test.getMessage() + }); + } + }) + } + } + }, + languageOptions: { + parser: "test/enhanced-parser" + }, + rules: { + "test/test-service-rule": 2 + } + }; + + const messages = linter.verify("0", config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].message, "Hi!"); + }); + + it("should use the same parserServices if source code object is reused", () => { + + const config = { + plugins: { + test: { + parsers: { + "enhanced-parser": testParsers.enhancedParser + }, + rules: { + "test-service-rule": context => ({ + Literal(node) { + context.report({ + node, + message: context.parserServices.test.getMessage() + }); + } + }) + } + } + }, + languageOptions: { + parser: "test/enhanced-parser" + }, + rules: { + "test/test-service-rule": 2 + } + }; + + const messages = linter.verify("0", config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].message, "Hi!"); + + const messages2 = linter.verify(linter.getSourceCode(), config, filename); + + assert.strictEqual(messages2.length, 1); + assert.strictEqual(messages2[0].message, "Hi!"); + }); + + it("should pass parser as context.languageOptions.parser to all rules when default parser is used", () => { + + // references to Espree get messed up in a browser context, so wrap it + const fakeParser = { + parse: espree.parse + }; + + const spy = sinon.spy(context => { + assert.strictEqual(context.languageOptions.parser, fakeParser); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + "test-rule": spy + } + } + }, + languageOptions: { + parser: fakeParser + }, + rules: { + "test/test-rule": 2 + } + }; + + linter.verify("0", config, filename); + assert.isTrue(spy.calledOnce); + }); + + + describe("Custom Parsers", () => { + + const errorPrefix = "Parsing error: "; + + it("should have file path passed to it", () => { + const code = "/* this is code */"; + const parseSpy = sinon.spy(testParsers.stubParser, "parse"); + const config = { + languageOptions: { + parser: testParsers.stubParser + } + }; + + linter.verify(code, config, filename, true); + + sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename }); + }); + + it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => { + const code = "var myDivElement =
;"; + const config = { + languageOptions: { + parser: esprima, + parserOptions: { + jsx: true + } + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => { + const code = "null %% 'foo'"; + const config = { + languageOptions: { + parser: testParsers.unknownLogicalOperator + } + }; + + // This shouldn't throw + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => { + const code = "foo && bar %% baz"; + const config = { + languageOptions: { + parser: testParsers.unknownLogicalOperatorNested + } + }; + + // This shouldn't throw + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not throw or return errors when the custom parser returns unknown AST nodes", () => { + const code = "foo && bar %% baz"; + const nodes = []; + const config = { + plugins: { + test: { + rules: { + "collect-node-types": () => ({ + "*"(node) { + nodes.push(node.type); + } + }) + } + } + }, + languageOptions: { + parser: testParsers.nonJSParser + }, + rules: { + "test/collect-node-types": "error" + } + }; + + const messages = linter.verify(code, config, filename, true); + + assert.strictEqual(messages.length, 0); + assert.isTrue(nodes.length > 0); + }); + + it("should strip leading line: prefix from parser error", () => { + const messages = linter.verify(";", { + languageOptions: { + parser: testParsers.lineError + } + }, "filename"); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError); + }); + + it("should not modify a parser error message without a leading line: prefix", () => { + const messages = linter.verify(";", { + languageOptions: { + parser: testParsers.noLineError + } + }, "filename"); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError); + }); + + describe("if a parser provides 'visitorKeys'", () => { + let types = []; + let sourceCode; + let scopeManager; + let firstChildNodes = []; + + beforeEach(() => { + types = []; + firstChildNodes = []; + const config = { + plugins: { + test: { + rules: { + "collect-node-types": () => ({ + "*"(node) { + types.push(node.type); + } + }), + "save-scope-manager": context => { + scopeManager = context.getSourceCode().scopeManager; + + return {}; + }, + "esquery-option": () => ({ + ":first-child"(node) { + firstChildNodes.push(node); + } + }) + } + } + }, + languageOptions: { + parser: testParsers.enhancedParser2 + }, + rules: { + "test/collect-node-types": "error", + "test/save-scope-manager": "error", + "test/esquery-option": "error" + } + }; + + linter.verify("@foo class A {}", config); + + sourceCode = linter.getSourceCode(); + }); + + it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => { + assert.deepStrictEqual( + types, + ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"] + ); + }); + + it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => { + assert.deepStrictEqual( + scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API + ["experimentalDecorators", "id", "superClass", "body"] + ); + }); + + it("should use the same visitorKeys if the source code object is reused", () => { + const types2 = []; + const config = { + plugins: { + test: { + rules: { + "collect-node-types": () => ({ + "*"(node) { + types2.push(node.type); + } + }) + } + } + }, + rules: { + "test/collect-node-types": "error" + } + }; + + linter.verify(sourceCode, config); + + assert.deepStrictEqual( + types2, + ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"] + ); + }); + + it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => { + assert.deepStrictEqual( + firstChildNodes, + [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]] + ); + }); + }); + + describe("if a parser provides 'scope'", () => { + let scope = null; + let sourceCode = null; + + beforeEach(() => { + const config = { + plugins: { + test: { + rules: { + "save-scope1": context => ({ + Program() { + scope = context.getScope(); + } + }) + } + } + }, + languageOptions: { + parser: testParsers.enhancedParser3 + }, + rules: { + "test/save-scope1": "error" + } + }; + + linter.verify("@foo class A {}", config); + + sourceCode = linter.getSourceCode(); + }); + + it("should use the scope (so the global scope has the reference of '@foo')", () => { + assert.strictEqual(scope.references.length, 1); + assert.deepStrictEqual( + scope.references[0].identifier.name, + "foo" + ); + }); + + it("should use the same scope if the source code object is reused", () => { + let scope2 = null; + const config = { + plugins: { + test: { + rules: { + "save-scope2": context => ({ + Program() { + scope2 = context.getScope(); + } + }) + } + } + }, + rules: { + "test/save-scope2": "error" + } + }; + + linter.verify(sourceCode, config, "test.js"); + + assert(scope2 !== null); + assert(scope2 === scope); + }); + }); + + it("should pass default languageOptions to the parser", () => { + + const spy = sinon.spy((code, options) => espree.parse(code, options)); + + linter.verify(";", { + languageOptions: { + parser: { + parse: spy + } + } + }, "filename.js"); + + assert(spy.calledWithMatch(";", { + ecmaVersion: espree.latestEcmaVersion + 2009, + sourceType: "module" + })); + }); + }); + + + }); + + describe("parseOptions", () => { + + it("should pass ecmaFeatures to all rules when provided on config", () => { + + const parserOptions = { + ecmaFeatures: { + jsx: true + } + }; + + const config = { + plugins: { + test: { + rules: { + "test-rule": sinon.mock().withArgs( + sinon.match({ languageOptions: { parserOptions } }) + ).returns({}) + } + } + }, + languageOptions: { + parserOptions + }, + rules: { + "test/test-rule": 2 + } + }; + + linter.verify("0", config, filename); + }); + + it("should switch globalReturn to false if sourceType is module", () => { + + const config = { + plugins: { + test: { + rules: { + "test-rule": sinon.mock().withArgs( + sinon.match({ + languageOptions: { + parserOptions: { + ecmaFeatures: { + globalReturn: false + } + } + } + }) + ).returns({}) + } + } + }, + languageOptions: { + sourceType: "module", + parserOptions: { + ecmaFeatures: { + globalReturn: true + } + } + }, + rules: { + "test/test-rule": 2 + } + }; + + linter.verify("0", config, filename); + }); + + it("should not parse sloppy-mode code when impliedStrict is true", () => { + + const messages = linter.verify("var private;", { + languageOptions: { + parserOptions: { + ecmaFeatures: { + impliedStrict: true + } + } + } + }, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved"); + }); + + it("should properly parse valid code when impliedStrict is true", () => { + + const messages = linter.verify("var foo;", { + languageOptions: { + parserOptions: { + ecmaFeatures: { + impliedStrict: true + } + } + } + }, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should properly parse JSX when passed ecmaFeatures", () => { + + const messages = linter.verify("var x =
;", { + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true + } + } + } + }, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should report an error when JSX code is encountered and JSX is not enabled", () => { + const code = "var myDivElement =
;"; + const messages = linter.verify(code, {}, "filename"); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].line, 1); + assert.strictEqual(messages[0].column, 20); + assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <"); + }); + + it("should not report an error when JSX code is encountered and JSX is enabled", () => { + const code = "var myDivElement =
;"; + const messages = linter.verify(code, { + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true + } + } + } + + }, "filename"); + + assert.strictEqual(messages.length, 0); + }); + + it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => { + const code = "var myDivElement =
;"; + const messages = linter.verify(code, { + languageOptions: { + ecmaVersion: 6, + parserOptions: { + ecmaFeatures: { + jsx: true + } + } + } + + }, "filename"); + + assert.strictEqual(messages.length, 0); + }); + + }); + + }); + + describe("settings", () => { + const ruleId = "test-rule"; + + it("should pass settings to all rules", () => { + + const config = { + plugins: { + test: { + rules: { + [ruleId]: context => ({ + Literal(node) { + context.report(node, context.settings.info); + } + }) + } + } + }, + settings: { + info: "Hello" + }, + rules: { + [`test/${ruleId}`]: 1 + } + }; + + const messages = linter.verify("0", config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].message, "Hello"); + }); + + it("should not have any settings if they were not passed in", () => { + + const config = { + plugins: { + test: { + rules: { + [ruleId]: context => ({ + Literal(node) { + if (Object.getOwnPropertyNames(context.settings).length !== 0) { + context.report(node, "Settings should be empty"); + } + } + }) + } + } + }, + settings: { + }, + rules: { + [`test/${ruleId}`]: 1 + } + }; + + const messages = linter.verify("0", config, filename); + + assert.strictEqual(messages.length, 0); + }); + }); + + describe("rules", () => { + const code = "var answer = 6 * 7"; + + it("should be configurable by only setting the integer value", () => { + const rule = "semi", + config = { rules: {} }; + + config.rules[rule] = 1; + + const messages = linter.verify(code, config, filename, true); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, rule); + }); + + it("should be configurable by only setting the string value", () => { + const rule = "semi", + config = { rules: {} }; + + config.rules[rule] = "warn"; + + const messages = linter.verify(code, config, filename, true); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 1); + assert.strictEqual(messages[0].ruleId, rule); + }); + + it("should be configurable by passing in values as an array", () => { + const rule = "semi", + config = { rules: {} }; + + config.rules[rule] = [1]; + + const messages = linter.verify(code, config, filename, true); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, rule); + }); + + it("should be configurable by passing in string value as an array", () => { + const rule = "semi", + config = { rules: {} }; + + config.rules[rule] = ["warn"]; + + const messages = linter.verify(code, config, filename, true); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 1); + assert.strictEqual(messages[0].ruleId, rule); + }); + + it("should not be configurable by setting other value", () => { + const rule = "semi", + config = { rules: {} }; + + config.rules[rule] = "1"; + + assert.throws(() => { + linter.verify(code, config, filename, true); + }, /Key "rules": Key "semi"/u); + }); + + it("should process empty config", () => { + const config = {}; + const messages = linter.verify(code, config, filename, true); + + assert.strictEqual(messages.length, 0); + }); + }); + + }); + + describe("verify()", () => { + + it("should report warnings in order by line and column when called", () => { + + const code = "foo()\n alert('test')"; + const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 3); + assert.strictEqual(messages[0].line, 1); + assert.strictEqual(messages[0].column, 6); + assert.strictEqual(messages[1].line, 2); + assert.strictEqual(messages[1].column, 18); + assert.strictEqual(messages[2].line, 2); + assert.strictEqual(messages[2].column, 18); + }); + + describe("Plugins", () => { + + it("should not load rule definition when rule isn't used", () => { + + const spy = sinon.spy(); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + } + }; + + linter.verify("code", config, filename); + assert.isTrue(spy.notCalled, "Rule should not have been called"); + }); + }); + + describe("Rule Internals", () => { + + const code = TEST_CODE; + + it("should throw an error when an error occurs inside of a rule visitor", () => { + const config = { + plugins: { + test: { + rules: { + checker: () => ({ + Program() { + throw new Error("Intentional error."); + } + }) + } + } + }, + rules: { "test/checker": "error" } + }; + + assert.throws(() => { + linter.verify(code, config, filename); + }, `Intentional error.\nOccurred while linting ${filename}:1\nRule: "test/checker"`); + }); + + it("should not call rule visitor with a `this` value", () => { + const spy = sinon.spy(); + const config = { + plugins: { + test: { + rules: { + checker: () => ({ + Program: spy + }) + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify("foo", config); + assert(spy.calledOnce); + assert.strictEqual(spy.firstCall.thisValue, void 0); + }); + + it("should not call unrecognized rule visitor when present in a rule", () => { + const spy = sinon.spy(); + const config = { + plugins: { + test: { + rules: { + checker: () => ({ + newListener: spy + }) + } + } + }, + rules: { + "test/checker": "error", + "no-undef": "error" + } + }; + + linter.verify("foo", config); + assert(spy.notCalled); + }); + + it("should have all the `parent` properties on nodes when the rule visitors are created", () => { + const spy = sinon.spy(context => { + const ast = context.getSourceCode().ast; + + assert.strictEqual(ast.body[0].parent, ast); + assert.strictEqual(ast.body[0].expression.parent, ast.body[0]); + assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression); + assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression); + + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify("foo + bar", config); + assert(spy.calledOnce); + }); + + it("events for each node type should fire", () => { + + // spies for various AST node types + const spyLiteral = sinon.spy(), + spyVariableDeclarator = sinon.spy(), + spyVariableDeclaration = sinon.spy(), + spyIdentifier = sinon.spy(), + spyBinaryExpression = sinon.spy(); + + const config = { + plugins: { + test: { + rules: { + checker() { + return { + Literal: spyLiteral, + VariableDeclarator: spyVariableDeclarator, + VariableDeclaration: spyVariableDeclaration, + Identifier: spyIdentifier, + BinaryExpression: spyBinaryExpression + }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + const messages = linter.verify(code, config, filename, true); + + assert.strictEqual(messages.length, 0); + sinon.assert.calledOnce(spyVariableDeclaration); + sinon.assert.calledOnce(spyVariableDeclarator); + sinon.assert.calledOnce(spyIdentifier); + sinon.assert.calledTwice(spyLiteral); + sinon.assert.calledOnce(spyBinaryExpression); + }); + + it("should throw an error if a rule reports a problem without a message", () => { + + const config = { + plugins: { + test: { + rules: { + "invalid-report"(context) { + return { + Program(node) { + context.report({ node }); + } + }; + } + } + } + }, + rules: { "test/invalid-report": "error" } + }; + + assert.throws( + () => linter.verify("foo", config), + TypeError, + "Missing `message` property in report() call; add a message that describes the linting problem." + ); + }); + + + }); + + describe("Rule Context", () => { + + describe("context.getFilename()", () => { + const ruleId = "filename-rule"; + + it("has access to the filename", () => { + + const config = { + plugins: { + test: { + rules: { + [ruleId]: context => ({ + Literal(node) { + context.report(node, context.getFilename()); + } + }) + } + } + }, + rules: { + [`test/${ruleId}`]: 1 + } + }; + + const messages = linter.verify("0", config, filename); + + assert.strictEqual(messages[0].message, filename); + }); + + it("defaults filename to ''", () => { + + const config = { + plugins: { + test: { + rules: { + [ruleId]: context => ({ + Literal(node) { + context.report(node, context.getFilename()); + } + }) + } + } + }, + rules: { + [`test/${ruleId}`]: 1 + } + }; + + + const messages = linter.verify("0", config); + + assert.strictEqual(messages[0].message, ""); + }); + }); + + describe("context.getPhysicalFilename()", () => { + + const ruleId = "filename-rule"; + + it("has access to the physicalFilename", () => { + + const config = { + plugins: { + test: { + rules: { + [ruleId]: context => ({ + Literal(node) { + context.report(node, context.getPhysicalFilename()); + } + }) + } + } + }, + rules: { + [`test/${ruleId}`]: 1 + } + }; + + const messages = linter.verify("0", config, filename); + + assert.strictEqual(messages[0].message, filename); + }); + + }); + + describe("context.getSourceLines()", () => { + + it("should get proper lines when using \\n as a line break", () => { + const code = "a;\nb;"; + const spy = sinon.spy(context => { + assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should get proper lines when using \\r\\n as a line break", () => { + const code = "a;\r\nb;"; + const spy = sinon.spy(context => { + assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should get proper lines when using \\r as a line break", () => { + const code = "a;\rb;"; + const spy = sinon.spy(context => { + assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should get proper lines when using \\u2028 as a line break", () => { + const code = "a;\u2028b;"; + const spy = sinon.spy(context => { + assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should get proper lines when using \\u2029 as a line break", () => { + const code = "a;\u2029b;"; + const spy = sinon.spy(context => { + assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + }); + + describe("context.getSource()", () => { + const code = TEST_CODE; + + it("should retrieve all text when used without parameters", () => { + + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + assert.strictEqual(context.getSource(), TEST_CODE); + }); + return { Program: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve all text for root node", () => { + + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(node => { + assert.strictEqual(context.getSource(node), TEST_CODE); + }); + return { Program: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should clamp to valid range when retrieving characters before start of source", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(node => { + assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE); + }); + return { Program: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve all text for binary expression", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(node => { + assert.strictEqual(context.getSource(node), "6 * 7"); + }); + return { BinaryExpression: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve all text plus two characters before for binary expression", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(node => { + assert.strictEqual(context.getSource(node, 2), "= 6 * 7"); + }); + return { BinaryExpression: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve all text plus one character after for binary expression", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(node => { + assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;"); + }); + return { BinaryExpression: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve all text plus two characters before and one character after for binary expression", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(node => { + assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;"); + }); + return { BinaryExpression: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + }); + + describe("context.getAncestors()", () => { + const code = TEST_CODE; + + it("should retrieve all ancestors when used", () => { + + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const ancestors = context.getAncestors(); + + assert.strictEqual(ancestors.length, 3); + }); + return { BinaryExpression: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config, filename, true); + assert(spy && spy.calledOnce); + }); + + it("should retrieve empty ancestors for root node", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const ancestors = context.getAncestors(); + + assert.strictEqual(ancestors.length, 0); + }); + + return { Program: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); + + describe("context.getNodeByRangeIndex()", () => { + const code = TEST_CODE; + + it("should retrieve a node starting at the given index", () => { + const spy = sinon.spy(context => { + assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should retrieve a node containing the given index", () => { + const spy = sinon.spy(context => { + assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should retrieve a node that is exactly the given index", () => { + const spy = sinon.spy(context => { + const node = context.getNodeByRangeIndex(13); + + assert.strictEqual(node.type, "Literal"); + assert.strictEqual(node.value, 6); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should retrieve a node ending with the given index", () => { + const spy = sinon.spy(context => { + assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should retrieve the deepest node containing the given index", () => { + const spy = sinon.spy(context => { + const node1 = context.getNodeByRangeIndex(14); + + assert.strictEqual(node1.type, "BinaryExpression"); + + const node2 = context.getNodeByRangeIndex(3); + + assert.strictEqual(node2.type, "VariableDeclaration"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + + it("should return null if the index is outside the range of any node", () => { + const spy = sinon.spy(context => { + const node1 = context.getNodeByRangeIndex(-1); + + assert.isNull(node1); + + const node2 = context.getNodeByRangeIndex(-99); + + assert.isNull(node2); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + }); + + describe("context.getScope()", () => { + const codeToTestScope = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });"; + + it("should retrieve the global scope correctly from a Program", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "global"); + }); + return { Program: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + linter.verify(codeToTestScope, config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve the function scope correctly from a FunctionDeclaration", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "function"); + }); + return { FunctionDeclaration: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + linter.verify(codeToTestScope, config); + assert(spy && spy.calledTwice); + }); + + it("should retrieve the function scope correctly from a LabeledStatement", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block.id.name, "foo"); + }); + return { LabeledStatement: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + + linter.verify(codeToTestScope, config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block.type, "ArrowFunctionExpression"); + }); + + return { ReturnStatement: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + + linter.verify(codeToTestScope, config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve the function scope correctly from within an SwitchStatement", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "switch"); + assert.strictEqual(scope.block.type, "SwitchStatement"); + }); + + return { SwitchStatement: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve the function scope correctly from within a BlockStatement", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "block"); + assert.strictEqual(scope.block.type, "BlockStatement"); + }); + + return { BlockStatement: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + + linter.verify("var x; {let y = 1}", config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve the function scope correctly from within a nested block statement", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "block"); + assert.strictEqual(scope.block.type, "BlockStatement"); + }); + + return { BlockStatement: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + + linter.verify("if (true) { let x = 1 }", config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve the function scope correctly from within a FunctionDeclaration", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block.type, "FunctionDeclaration"); + }); + + return { FunctionDeclaration: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + + linter.verify("function foo() {}", config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve the function scope correctly from within a FunctionExpression", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block.type, "FunctionExpression"); + }); + + return { FunctionExpression: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + + linter.verify("(function foo() {})();", config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve the catch scope correctly from within a CatchClause", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "catch"); + assert.strictEqual(scope.block.type, "CatchClause"); + }); + + return { CatchClause: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6 + }, + rules: { "test/checker": "error" } + }; + + linter.verify("try {} catch (err) {}", config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve module scope correctly from an ES6 module", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "module"); + }); + + return { AssignmentExpression: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6, + sourceType: "module" + }, + rules: { "test/checker": "error" } + }; + + + linter.verify("var foo = {}; foo.bar = 1;", config); + assert(spy && spy.calledOnce); + }); + + it("should retrieve function scope correctly when sourceType is commonjs", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(scope.type, "function"); + }); + + return { AssignmentExpression: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6, + sourceType: "commonjs" + }, + rules: { "test/checker": "error" } + }; + + linter.verify("var foo = {}; foo.bar = 1;", config); + assert(spy && spy.calledOnce); + }); + + describe("Scope Internals", () => { + + /** + * Get the scope on the node `astSelector` specified. + * @param {string} codeToEvaluate The source code to verify. + * @param {string} astSelector The AST selector to get scope. + * @param {number} [ecmaVersion=5] The ECMAScript version. + * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope. + */ + function getScope(codeToEvaluate, astSelector, ecmaVersion = 5) { + let node, scope; + + const config = { + plugins: { + test: { + rules: { + "get-scope": context => ({ + [astSelector](node0) { + node = node0; + scope = context.getScope(); + } + }) + } + } + }, + languageOptions: { + ecmaVersion, + sourceType: "script" + }, + rules: { "test/get-scope": "error" } + }; + + linter.verify( + codeToEvaluate, + config + ); + + return { node, scope }; + } + + it("should return 'function' scope on FunctionDeclaration (ES5)", () => { + const { node, scope } = getScope("function f() {}", "FunctionDeclaration"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node); + }); + + it("should return 'function' scope on FunctionExpression (ES5)", () => { + const { node, scope } = getScope("!function f() {}", "FunctionExpression"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node); + }); + + it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => { + const { node, scope } = getScope("function f() {}", "BlockStatement"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent); + }); + + it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => { + const { node, scope } = getScope("function f() {}", "BlockStatement", 2015); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent); + }); + + it("should return 'function' scope on BlockStatement in functions (ES5)", () => { + const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]); + }); + + it("should return 'block' scope on BlockStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015); + + assert.strictEqual(scope.type, "block"); + assert.strictEqual(scope.upper.type, "function"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]); + assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]); + }); + + it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015); + + assert.strictEqual(scope.type, "block"); + assert.strictEqual(scope.upper.type, "block"); + assert.strictEqual(scope.upper.upper.type, "function"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]); + assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]); + assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]); + }); + + it("should return 'function' scope on SwitchStatement in functions (ES5)", () => { + const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]); + }); + + it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015); + + assert.strictEqual(scope.type, "switch"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]); + }); + + it("should return 'function' scope on SwitchCase in functions (ES5)", () => { + const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent.parent.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]); + }); + + it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015); + + assert.strictEqual(scope.type, "switch"); + assert.strictEqual(scope.block, node.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]); + }); + + it("should return 'catch' scope on CatchClause in functions (ES5)", () => { + const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause"); + + assert.strictEqual(scope.type, "catch"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]); + }); + + it("should return 'catch' scope on CatchClause in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015); + + assert.strictEqual(scope.type, "catch"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]); + }); + + it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => { + const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement"); + + assert.strictEqual(scope.type, "catch"); + assert.strictEqual(scope.block, node.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]); + }); + + it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015); + + assert.strictEqual(scope.type, "block"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]); + }); + + it("should return 'function' scope on ForStatement in functions (ES5)", () => { + const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]); + }); + + it("should return 'for' scope on ForStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015); + + assert.strictEqual(scope.type, "for"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]); + }); + + it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => { + const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent.parent.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]); + }); + + it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015); + + assert.strictEqual(scope.type, "block"); + assert.strictEqual(scope.upper.type, "for"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), []); + assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]); + }); + + it("should return 'function' scope on ForInStatement in functions (ES5)", () => { + const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]); + }); + + it("should return 'for' scope on ForInStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015); + + assert.strictEqual(scope.type, "for"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]); + }); + + it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => { + const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement"); + + assert.strictEqual(scope.type, "function"); + assert.strictEqual(scope.block, node.parent.parent.parent); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]); + }); + + it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015); + + assert.strictEqual(scope.type, "block"); + assert.strictEqual(scope.upper.type, "for"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), []); + assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]); + }); + + it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015); + + assert.strictEqual(scope.type, "for"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]); + }); + + it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => { + const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015); + + assert.strictEqual(scope.type, "block"); + assert.strictEqual(scope.upper.type, "for"); + assert.strictEqual(scope.block, node); + assert.deepStrictEqual(scope.variables.map(v => v.name), []); + assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]); + }); + + it("should shadow the same name variable by the iteration variable.", () => { + const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015); + + assert.strictEqual(scope.type, "for"); + assert.strictEqual(scope.upper.type, "global"); + assert.strictEqual(scope.block, node); + assert.strictEqual(scope.upper.variables[0].references.length, 0); + assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id); + assert.strictEqual(scope.references[1].identifier, node.right); + assert.strictEqual(scope.references[1].resolved, scope.variables[0]); + }); + }); + + describe("Variables and references", () => { + const code = [ + "a;", + "function foo() { b; }", + "Object;", + "foo;", + "var c;", + "c;", + "/* global d */", + "d;", + "e;", + "f;" + ].join("\n"); + let scope = null; + + beforeEach(() => { + let ok = false; + + const config = { + plugins: { + test: { + rules: { + test(context) { + return { + Program() { + scope = context.getScope(); + ok = true; + } + }; + } + } + } + }, + languageOptions: { + globals: { e: true, f: false }, + sourceType: "script", + ecmaVersion: 5 + }, + rules: { + "test/test": 2 + } + }; + + linter.verify(code, config); + assert(ok); + }); + + afterEach(() => { + scope = null; + }); + + it("Scope#through should contain references of undefined variables", () => { + assert.strictEqual(scope.through.length, 2); + assert.strictEqual(scope.through[0].identifier.name, "a"); + assert.strictEqual(scope.through[0].identifier.loc.start.line, 1); + assert.strictEqual(scope.through[0].resolved, null); + assert.strictEqual(scope.through[1].identifier.name, "b"); + assert.strictEqual(scope.through[1].identifier.loc.start.line, 2); + assert.strictEqual(scope.through[1].resolved, null); + }); + + it("Scope#variables should contain global variables", () => { + assert(scope.variables.some(v => v.name === "Object")); + assert(scope.variables.some(v => v.name === "foo")); + assert(scope.variables.some(v => v.name === "c")); + assert(scope.variables.some(v => v.name === "d")); + assert(scope.variables.some(v => v.name === "e")); + assert(scope.variables.some(v => v.name === "f")); + }); + + it("Scope#set should contain global variables", () => { + assert(scope.set.get("Object")); + assert(scope.set.get("foo")); + assert(scope.set.get("c")); + assert(scope.set.get("d")); + assert(scope.set.get("e")); + assert(scope.set.get("f")); + }); + + it("Variables#references should contain their references", () => { + assert.strictEqual(scope.set.get("Object").references.length, 1); + assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object"); + assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3); + assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object")); + assert.strictEqual(scope.set.get("foo").references.length, 1); + assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo"); + assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4); + assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo")); + assert.strictEqual(scope.set.get("c").references.length, 1); + assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c"); + assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6); + assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c")); + assert.strictEqual(scope.set.get("d").references.length, 1); + assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d"); + assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8); + assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d")); + assert.strictEqual(scope.set.get("e").references.length, 1); + assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e"); + assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9); + assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e")); + assert.strictEqual(scope.set.get("f").references.length, 1); + assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f"); + assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10); + assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f")); + }); + + it("Reference#resolved should be their variable", () => { + assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object")); + assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo")); + assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c")); + assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d")); + assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e")); + assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f")); + }); + }); + }); + + describe("context.getDeclaredVariables(node)", () => { + + /** + * Assert `context.getDeclaredVariables(node)` is valid. + * @param {string} code A code to check. + * @param {string} type A type string of ASTNode. This method checks variables on the node of the type. + * @param {Array>} expectedNamesList An array of expected variable names. The expected variable names is an array of string. + * @returns {void} + */ + function verify(code, type, expectedNamesList) { + const config = { + plugins: { + test: { + + rules: { + test(context) { + + /** + * Assert `context.getDeclaredVariables(node)` is empty. + * @param {ASTNode} node A node to check. + * @returns {void} + */ + function checkEmpty(node) { + assert.strictEqual(0, context.getDeclaredVariables(node).length); + } + const rule = { + Program: checkEmpty, + EmptyStatement: checkEmpty, + BlockStatement: checkEmpty, + ExpressionStatement: checkEmpty, + LabeledStatement: checkEmpty, + BreakStatement: checkEmpty, + ContinueStatement: checkEmpty, + WithStatement: checkEmpty, + SwitchStatement: checkEmpty, + ReturnStatement: checkEmpty, + ThrowStatement: checkEmpty, + TryStatement: checkEmpty, + WhileStatement: checkEmpty, + DoWhileStatement: checkEmpty, + ForStatement: checkEmpty, + ForInStatement: checkEmpty, + DebuggerStatement: checkEmpty, + ThisExpression: checkEmpty, + ArrayExpression: checkEmpty, + ObjectExpression: checkEmpty, + Property: checkEmpty, + SequenceExpression: checkEmpty, + UnaryExpression: checkEmpty, + BinaryExpression: checkEmpty, + AssignmentExpression: checkEmpty, + UpdateExpression: checkEmpty, + LogicalExpression: checkEmpty, + ConditionalExpression: checkEmpty, + CallExpression: checkEmpty, + NewExpression: checkEmpty, + MemberExpression: checkEmpty, + SwitchCase: checkEmpty, + Identifier: checkEmpty, + Literal: checkEmpty, + ForOfStatement: checkEmpty, + ArrowFunctionExpression: checkEmpty, + YieldExpression: checkEmpty, + TemplateLiteral: checkEmpty, + TaggedTemplateExpression: checkEmpty, + TemplateElement: checkEmpty, + ObjectPattern: checkEmpty, + ArrayPattern: checkEmpty, + RestElement: checkEmpty, + AssignmentPattern: checkEmpty, + ClassBody: checkEmpty, + MethodDefinition: checkEmpty, + MetaProperty: checkEmpty + }; + + rule[type] = function(node) { + const expectedNames = expectedNamesList.shift(); + const variables = context.getDeclaredVariables(node); + + assert(Array.isArray(expectedNames)); + assert(Array.isArray(variables)); + assert.strictEqual(expectedNames.length, variables.length); + for (let i = variables.length - 1; i >= 0; i--) { + assert.strictEqual(expectedNames[i], variables[i].name); + } + }; + return rule; + } + } + + } + }, + languageOptions: { + ecmaVersion: 6, + sourceType: "module" + }, + rules: { + "test/test": 2 + } + }; + + linter.verify(code, config); + + // Check all expected names are asserted. + assert.strictEqual(0, expectedNamesList.length); + } + + it("VariableDeclaration", () => { + const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n "; + const namesList = [ + ["a", "b", "c"], + ["d", "e", "f"], + ["g", "h", "i", "j", "k"], + ["l"] + ]; + + verify(code, "VariableDeclaration", namesList); + }); + + it("VariableDeclaration (on for-in/of loop)", () => { + + // TDZ scope is created here, so tests to exclude those. + const code = "\n for (var {a, x: [b], y: {c = 0}} in foo) {\n let g;\n }\n for (let {d, x: [e], y: {f = 0}} of foo) {\n let h;\n }\n "; + const namesList = [ + ["a", "b", "c"], + ["g"], + ["d", "e", "f"], + ["h"] + ]; + + verify(code, "VariableDeclaration", namesList); + }); + + it("VariableDeclarator", () => { + + // TDZ scope is created here, so tests to exclude those. + const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n "; + const namesList = [ + ["a", "b", "c"], + ["d", "e", "f"], + ["g", "h", "i"], + ["j", "k"], + ["l"] + ]; + + verify(code, "VariableDeclarator", namesList); + }); + + it("FunctionDeclaration", () => { + const code = "\n function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n }\n function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n }\n "; + const namesList = [ + ["foo", "a", "b", "c", "d", "e"], + ["bar", "f", "g", "h", "i", "j"] + ]; + + verify(code, "FunctionDeclaration", namesList); + }); + + it("FunctionExpression", () => { + const code = "\n (function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n });\n (function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n });\n "; + const namesList = [ + ["foo", "a", "b", "c", "d", "e"], + ["bar", "f", "g", "h", "i", "j"], + ["q"] + ]; + + verify(code, "FunctionExpression", namesList); + }); + + it("ArrowFunctionExpression", () => { + const code = "\n (({a, x: [b], y: {c = 0}}, [d, e]) => {\n let z;\n });\n (({f, x: [g], y: {h = 0}}, [i, j]) => {\n let z;\n });\n "; + const namesList = [ + ["a", "b", "c", "d", "e"], + ["f", "g", "h", "i", "j"] + ]; + + verify(code, "ArrowFunctionExpression", namesList); + }); + + it("ClassDeclaration", () => { + const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n "; + const namesList = [ + ["A", "A"], // outer scope's and inner scope's. + ["B", "B"] + ]; + + verify(code, "ClassDeclaration", namesList); + }); + + it("ClassExpression", () => { + const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n "; + const namesList = [ + ["A"], + ["B"] + ]; + + verify(code, "ClassExpression", namesList); + }); + + it("CatchClause", () => { + const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n "; + const namesList = [ + ["a", "b"], + ["c", "d"] + ]; + + verify(code, "CatchClause", namesList); + }); + + it("ImportDeclaration", () => { + const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n "; + const namesList = [ + [], + ["a"], + ["b", "c", "d"] + ]; + + verify(code, "ImportDeclaration", namesList); + }); + + it("ImportSpecifier", () => { + const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n "; + const namesList = [ + ["c"], + ["d"] + ]; + + verify(code, "ImportSpecifier", namesList); + }); + + it("ImportDefaultSpecifier", () => { + const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n "; + const namesList = [ + ["b"] + ]; + + verify(code, "ImportDefaultSpecifier", namesList); + }); + + it("ImportNamespaceSpecifier", () => { + const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n "; + const namesList = [ + ["a"] + ]; + + verify(code, "ImportNamespaceSpecifier", namesList); + }); + }); + + describe("context.markVariableAsUsed()", () => { + + it("should mark variables in current scope as used", () => { + const code = "var a = 1, b = 2;"; + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + assert.isTrue(context.markVariableAsUsed("a")); + + const scope = context.getScope(); + + assert.isTrue(getVariable(scope, "a").eslintUsed); + assert.notOk(getVariable(scope, "b").eslintUsed); + }); + + return { "Program:exit": spy }; + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should mark variables in function args as used", () => { + const code = "function abc(a, b) { return 1; }"; + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + assert.isTrue(context.markVariableAsUsed("a")); + + const scope = context.getScope(); + + assert.isTrue(getVariable(scope, "a").eslintUsed); + assert.notOk(getVariable(scope, "b").eslintUsed); + }); + + return { ReturnStatement: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should mark variables in higher scopes as used", () => { + const code = "var a, b; function abc() { return 1; }"; + let returnSpy, exitSpy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + returnSpy = sinon.spy(() => { + assert.isTrue(context.markVariableAsUsed("a")); + }); + exitSpy = sinon.spy(() => { + const scope = context.getScope(); + + assert.isTrue(getVariable(scope, "a").eslintUsed); + assert.notOk(getVariable(scope, "b").eslintUsed); + }); + + return { ReturnStatement: returnSpy, "Program:exit": exitSpy }; + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(returnSpy && returnSpy.calledOnce); + assert(exitSpy && exitSpy.calledOnce); + }); + + it("should mark variables as used when sourceType is commonjs", () => { + const code = "var a = 1, b = 2;"; + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const globalScope = context.getScope(), + childScope = globalScope.childScopes[0]; + + assert.isTrue(context.markVariableAsUsed("a"), "Call to markVariableAsUsed should return true"); + + assert.isTrue(getVariable(childScope, "a").eslintUsed, "'a' should be marked as used."); + assert.isUndefined(getVariable(childScope, "b").eslintUsed, "'b' should be marked as used."); + }); + + return { "Program:exit": spy }; + } + } + } + }, + languageOptions: { + sourceType: "commonjs" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce, "Spy wasn't called."); + }); + + it("should mark variables in modules as used", () => { + const code = "var a = 1, b = 2;"; + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const globalScope = context.getScope(), + childScope = globalScope.childScopes[0]; + + assert.isTrue(context.markVariableAsUsed("a")); + + assert.isTrue(getVariable(childScope, "a").eslintUsed); + assert.isUndefined(getVariable(childScope, "b").eslintUsed); + }); + + return { "Program:exit": spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6, + sourceType: "module" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should return false if the given variable is not found", () => { + const code = "var a = 1, b = 2;"; + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + assert.isFalse(context.markVariableAsUsed("c")); + }); + + return { "Program:exit": spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); + + describe("context.getCwd()", () => { + const code = "a;\nb;"; + const baseConfig = { rules: { "test/checker": "error" } }; + + it("should get cwd correctly in the context", () => { + const cwd = "cwd"; + const linterWithOption = new Linter({ cwd, configType: "flat" }); + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + assert.strictEqual(context.getCwd(), cwd); + }); + return { Program: spy }; + } + } + } + }, + ...baseConfig + }; + + linterWithOption.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should assign process.cwd() to it if cwd is undefined", () => { + + const linterWithOption = new Linter({ configType: "flat" }); + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + + spy = sinon.spy(() => { + assert.strictEqual(context.getCwd(), process.cwd()); + }); + return { Program: spy }; + } + } + } + }, + ...baseConfig + }; + + linterWithOption.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("should assign process.cwd() to it if the option is undefined", () => { + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + + spy = sinon.spy(() => { + assert.strictEqual(context.getCwd(), process.cwd()); + }); + return { Program: spy }; + } + } + } + }, + ...baseConfig + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); + + }); + + describe("Rule Severity", () => { + + it("rule should run as warning when set to 1 with a config array", () => { + const ruleId = "semi", + configs = createFlatConfigArray({ + files: ["**/*.js"], + rules: { + [ruleId]: 1 + } + }); + + configs.normalizeSync(); + const messages = linter.verify("foo", configs, filename, true); + + assert.strictEqual(messages.length, 1, "Message length is wrong"); + assert.strictEqual(messages[0].ruleId, ruleId); + }); + + it("rule should run as warning when set to 1 with a plain array", () => { + const ruleId = "semi", + configs = [{ + files: ["**/*.js"], + rules: { + [ruleId]: 1 + } + }]; + + const messages = linter.verify("foo", configs, filename, true); + + assert.strictEqual(messages.length, 1, "Message length is wrong"); + assert.strictEqual(messages[0].ruleId, ruleId); + }); + + it("rule should run as warning when set to 1 with an object", () => { + const ruleId = "semi", + config = { + files: ["**/*.js"], + rules: { + [ruleId]: 1 + } + }; + + const messages = linter.verify("foo", config, filename, true); + + assert.strictEqual(messages.length, 1, "Message length is wrong"); + assert.strictEqual(messages[0].ruleId, ruleId); + }); + }); + + describe("Code with a hashbang comment", () => { + const code = "#!bin/program\n\nvar foo;;"; + + it("should preserve line numbers", () => { + const config = { rules: { "no-extra-semi": 1 } }; + const messages = linter.verify(code, config); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-extra-semi"); + assert.strictEqual(messages[0].nodeType, "EmptyStatement"); + assert.strictEqual(messages[0].line, 3); + }); + + it("should have a comment with the hashbang in it", () => { + const spy = sinon.spy(context => { + const comments = context.getAllComments(); + + assert.strictEqual(comments.length, 1); + assert.strictEqual(comments[0].type, "Shebang"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: spy + } + } + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify(code, config); + assert(spy.calledOnce); + }); + }); + + describe("Options", () => { + + describe("filename", () => { + it("should allow filename to be passed on options object", () => { + const filenameChecker = sinon.spy(context => { + assert.strictEqual(context.getFilename(), "foo.js"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: filenameChecker + } + } + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify("foo;", config, { filename: "foo.js" }); + assert(filenameChecker.calledOnce); + }); + + it("should allow filename to be passed as third argument", () => { + const filenameChecker = sinon.spy(context => { + assert.strictEqual(context.getFilename(), "bar.js"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: filenameChecker + } + } + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify("foo;", config, "bar.js"); + assert(filenameChecker.calledOnce); + }); + + it("should default filename to when options object doesn't have filename", () => { + const filenameChecker = sinon.spy(context => { + assert.strictEqual(context.getFilename(), ""); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: filenameChecker + } + } + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify("foo;", config, {}); + assert(filenameChecker.calledOnce); + }); + + it("should default filename to when only two arguments are passed", () => { + const filenameChecker = sinon.spy(context => { + assert.strictEqual(context.getFilename(), ""); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: filenameChecker + } + } + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify("foo;", config); + assert(filenameChecker.calledOnce); + }); + }); + + describe("physicalFilename", () => { + it("should be same as `filename` passed on options object, if no processors are used", () => { + const physicalFilenameChecker = sinon.spy(context => { + assert.strictEqual(context.getPhysicalFilename(), "foo.js"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: physicalFilenameChecker + } + } + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify("foo;", config, { filename: "foo.js" }); + assert(physicalFilenameChecker.calledOnce); + }); + + it("should default physicalFilename to when options object doesn't have filename", () => { + const physicalFilenameChecker = sinon.spy(context => { + assert.strictEqual(context.getPhysicalFilename(), ""); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: physicalFilenameChecker + } + } + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify("foo;", config, {}); + assert(physicalFilenameChecker.calledOnce); + }); + + it("should default physicalFilename to when only two arguments are passed", () => { + const physicalFilenameChecker = sinon.spy(context => { + assert.strictEqual(context.getPhysicalFilename(), ""); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + checker: physicalFilenameChecker + } + } + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify("foo;", config); + assert(physicalFilenameChecker.calledOnce); + }); + }); + + }); + + describe("Inline Directives", () => { + + describe("/*global*/ Comments", () => { + + describe("when evaluating code containing /*global */ and /*globals */ blocks", () => { + + it("variables should be available in global scope", () => { + const code = ` + /*global a b:true c:false d:readable e:writeable Math:off */ + function foo() {} + /*globals f:true*/ + /* global ConfigGlobal : readable */ + `; + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + const a = getVariable(scope, "a"), + b = getVariable(scope, "b"), + c = getVariable(scope, "c"), + d = getVariable(scope, "d"), + e = getVariable(scope, "e"), + f = getVariable(scope, "f"), + mathGlobal = getVariable(scope, "Math"), + arrayGlobal = getVariable(scope, "Array"), + configGlobal = getVariable(scope, "ConfigGlobal"); + + assert.strictEqual(a.name, "a"); + assert.strictEqual(a.writeable, false); + assert.strictEqual(b.name, "b"); + assert.strictEqual(b.writeable, true); + assert.strictEqual(c.name, "c"); + assert.strictEqual(c.writeable, false); + assert.strictEqual(d.name, "d"); + assert.strictEqual(d.writeable, false); + assert.strictEqual(e.name, "e"); + assert.strictEqual(e.writeable, true); + assert.strictEqual(f.name, "f"); + assert.strictEqual(f.writeable, true); + assert.strictEqual(mathGlobal, null); + assert.strictEqual(arrayGlobal, null); + assert.strictEqual(configGlobal.name, "ConfigGlobal"); + assert.strictEqual(configGlobal.writeable, false); + }); + + return { Program: spy }; + } + } + } + }, + rules: { "test/checker": "error" }, + languageOptions: { + globals: { Array: "off", ConfigGlobal: "writeable" } + } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); + + describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => { + const code = "/* global a b : true c: false*/"; + + it("variables should be available in global scope", () => { + + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(), + a = getVariable(scope, "a"), + b = getVariable(scope, "b"), + c = getVariable(scope, "c"); + + assert.strictEqual(a.name, "a"); + assert.strictEqual(a.writeable, false); + assert.strictEqual(b.name, "b"); + assert.strictEqual(b.writeable, true); + assert.strictEqual(c.name, "c"); + assert.strictEqual(c.writeable, false); + }); + + return { Program: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); + + describe("when evaluating code containing a line comment", () => { + const code = "//global a \n function f() {}"; + + it("should not introduce a global variable", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(getVariable(scope, "a"), null); + }); + + return { Program: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); + + describe("when evaluating code containing normal block comments", () => { + const code = "/**/ /*a*/ /*b:true*/ /*foo c:false*/"; + + it("should not introduce a global variable", () => { + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.strictEqual(getVariable(scope, "a"), null); + assert.strictEqual(getVariable(scope, "b"), null); + assert.strictEqual(getVariable(scope, "foo"), null); + assert.strictEqual(getVariable(scope, "c"), null); + }); + + return { Program: spy }; + } + } + } + }, + rules: { "test/checker": "error" } + }; + + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); + + it("should attach a \"/*global\" comment node to declared variables", () => { + const code = "/* global foo */\n/* global bar, baz */"; + let ok = false; + const config = { + plugins: { + test: { + rules: { + test(context) { + return { + Program() { + const scope = context.getScope(); + const sourceCode = context.getSourceCode(); + const comments = sourceCode.getAllComments(); + + assert.strictEqual(2, comments.length); + + const foo = getVariable(scope, "foo"); + + assert.strictEqual(foo.eslintExplicitGlobal, true); + assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]); + + const bar = getVariable(scope, "bar"); + + assert.strictEqual(bar.eslintExplicitGlobal, true); + assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]); + + const baz = getVariable(scope, "baz"); + + assert.strictEqual(baz.eslintExplicitGlobal, true); + assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]); + + ok = true; + } + }; + } + } + } + }, + rules: { "test/test": "error" } + }; + + + linter.verify(code, config); + assert(ok); + }); + + it("should report a linting error when a global is set to an invalid value", () => { + const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } }); + + assert.deepStrictEqual(results, [ + { + ruleId: null, + severity: 2, + message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')", + line: 1, + column: 1, + endLine: 1, + endColumn: 39, + nodeType: null + }, + { + ruleId: "no-undef", + messageId: "undef", + severity: 2, + message: "'foo' is not defined.", + line: 2, + column: 1, + endLine: 2, + endColumn: 4, + nodeType: "Identifier" + } + ]); + }); + + }); + + describe("/*exported*/ Comments", () => { + + it("we should behave nicely when no matching variable is found", () => { + const code = "/* exported horse */"; + const config = { rules: {} }; + + linter.verify(code, config, filename, true); + }); + + it("variables should be exported", () => { + const code = "/* exported horse */\n\nvar horse = 'circus'"; + let spy; + + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(), + horse = getVariable(scope, "horse"); + + assert.strictEqual(horse.eslintUsed, true); + }); + + return { Program: spy }; + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("undefined variables should not be exported", () => { + const code = "/* exported horse */\n\nhorse = 'circus'"; + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(), + horse = getVariable(scope, "horse"); + + assert.strictEqual(horse, null); + }); + + return { Program: spy }; + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("variables should be exported in strict mode", () => { + const code = "/* exported horse */\n'use strict';\nvar horse = 'circus'"; + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(), + horse = getVariable(scope, "horse"); + + assert.strictEqual(horse.eslintUsed, true); + }); + + return { Program: spy }; + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("variables should not be exported in the es6 module environment", () => { + const code = "/* exported horse */\nvar horse = 'circus'"; + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(), + horse = getVariable(scope, "horse"); + + assert.strictEqual(horse, null); // there is no global scope at all + }); + + return { Program: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 6, + sourceType: "module" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + it("variables should not be exported when in a commonjs file", () => { + const code = "/* exported horse */\nvar horse = 'circus'"; + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(), + horse = getVariable(scope, "horse"); + + assert.strictEqual(horse, null); // there is no global scope at all + }); + + return { Program: spy }; + } + } + } + }, + languageOptions: { + sourceType: "commonjs" + }, + rules: { "test/checker": "error" } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + }); + + describe("/*eslint*/ Comments", () => { + describe("when evaluating code with comments to enable rules", () => { + + it("should report a violation", () => { + const code = "/*eslint no-alert:1*/ alert('test');"; + const config = { rules: {} }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].message, "Unexpected alert."); + assert.include(messages[0].nodeType, "CallExpression"); + }); + + it("rules should not change initial config", () => { + const config = { + languageOptions: { + sourceType: "script" + }, + rules: { strict: 2 } + }; + const codeA = "/*eslint strict: 0*/ function bar() { return 2; }"; + const codeB = "function foo() { return 1; }"; + let messages = linter.verify(codeA, config, filename, false); + + assert.strictEqual(messages.length, 0); + + messages = linter.verify(codeB, config, filename, false); + assert.strictEqual(messages.length, 1); + }); + + it("rules should not change initial config", () => { + const config = { + languageOptions: { + sourceType: "script" + }, + rules: { quotes: [2, "double"] } + }; + const codeA = "/*eslint quotes: 0*/ function bar() { return '2'; }"; + const codeB = "function foo() { return '1'; }"; + let messages = linter.verify(codeA, config, filename, false); + + assert.strictEqual(messages.length, 0); + + messages = linter.verify(codeB, config, filename, false); + assert.strictEqual(messages.length, 1); + }); + + it("rules should not change initial config", () => { + const config = { rules: { quotes: [2, "double"] } }; + const codeA = "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }"; + const codeB = "function foo() { return '1'; }"; + let messages = linter.verify(codeA, config, filename, false); + + assert.strictEqual(messages.length, 0); + + messages = linter.verify(codeB, config, filename, false); + assert.strictEqual(messages.length, 1); + }); + + it("rules should not change initial config", () => { + const config = { + languageOptions: { + sourceType: "script" + }, + rules: { "no-unused-vars": [2, { vars: "all" }] } + }; + const codeA = "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;"; + const codeB = "var b = 55;"; + let messages = linter.verify(codeA, config, filename, false); + + assert.strictEqual(messages.length, 0); + + messages = linter.verify(codeB, config, filename, false); + assert.strictEqual(messages.length, 1); + }); + }); + + describe("when evaluating code with invalid comments to enable rules", () => { + it("should report a violation when the config is not a valid rule configuration", () => { + assert.deepStrictEqual( + linter.verify("/*eslint no-alert:true*/ alert('test');", {}), + [ + { + severity: 2, + ruleId: "no-alert", + message: "Configuration for rule \"no-alert\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed 'true').\n", + line: 1, + column: 1, + endLine: 1, + endColumn: 25, + nodeType: null + } + ] + ); + }); + + it("should report a violation when the config violates a rule's schema", () => { + assert.deepStrictEqual( + linter.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {}), + [ + { + severity: 2, + ruleId: "no-alert", + message: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n", + line: 1, + column: 1, + endLine: 1, + endColumn: 63, + nodeType: null + } + ] + ); + }); + }); + + describe("when evaluating code with comments to disable rules", () => { + + it("should not report a violation", () => { + const config = { rules: { "no-alert": 1 } }; + const messages = linter.verify("/*eslint no-alert:0*/ alert('test');", config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should report an error when disabling a non-existent rule in inline comment", () => { + let code = "/*eslint foo:0*/ ;"; + let messages = linter.verify(code, {}, filename); + + assert.strictEqual(messages.length, 1, "/*eslint*/ comment should report problem."); + assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found."); + + code = "/*eslint-disable foo*/ ;"; + messages = linter.verify(code, {}, filename); + assert.strictEqual(messages.length, 1, "/*eslint-disable*/ comment should report problem."); + assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found."); + + code = "/*eslint-disable-line foo*/ ;"; + messages = linter.verify(code, {}, filename); + assert.strictEqual(messages.length, 1, "/*eslint-disable-line*/ comment should report problem."); + assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found."); + + code = "/*eslint-disable-next-line foo*/ ;"; + messages = linter.verify(code, {}, filename); + assert.strictEqual(messages.length, 1, "/*eslint-disable-next-line*/ comment should report problem."); + assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found."); + }); + + it("should not report an error, when disabling a non-existent rule in config", () => { + const messages = linter.verify("", { rules: { foo: 0 } }, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should throw an error when a non-existent rule in config", () => { + assert.throws(() => { + linter.verify("", { rules: { foo: 1 } }, filename); + }, /Key "rules": Key "foo":/u); + + assert.throws(() => { + linter.verify("", { rules: { foo: 2 } }, filename); + }, /Key "rules": Key "foo":/u); + + }); + }); + + describe("when evaluating code with comments to enable multiple rules", () => { + const code = "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');"; + + it("should report a violation", () => { + const config = { rules: {} }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].message, "Unexpected alert."); + assert.include(messages[0].nodeType, "CallExpression"); + assert.strictEqual(messages[1].ruleId, "no-console"); + }); + }); + + describe("when evaluating code with comments to enable and disable multiple rules", () => { + const code = "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');"; + + it("should report a violation", () => { + const config = { rules: { "no-console": 1, "no-alert": 0 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].message, "Unexpected alert."); + assert.include(messages[0].nodeType, "CallExpression"); + }); + }); + + describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => { + + let baseConfig; + + beforeEach(() => { + baseConfig = { + plugins: { + "test-plugin": { + rules: { + "test-rule"(context) { + return { + Literal(node) { + if (node.value === "trigger violation") { + context.report(node, "Reporting violation."); + } + } + }; + } + } + } + } + }; + + }); + + it("should not report a violation when inline comment enables plugin rule and there's no violation", () => { + const config = { ...baseConfig, rules: {} }; + const code = "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";"; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not report a violation when inline comment disables plugin rule", () => { + const code = "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\""; + const config = { ...baseConfig, rules: { "test-plugin/test-rule": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should report a violation when the report is right before the comment", () => { + const code = " /* eslint-disable */ "; + + const config = { + plugins: { + test: { + rules: { + checker: context => ({ + Program() { + context.report({ loc: { line: 1, column: 0 }, message: "foo" }); + } + }) + } + } + }, + rules: { + "test/checker": "error" + } + }; + + const problems = linter.verify(code, config); + + assert.strictEqual(problems.length, 1); + assert.strictEqual(problems[0].message, "foo"); + }); + + it("should not report a violation when the report is right at the start of the comment", () => { + const code = " /* eslint-disable */ "; + + const config = { + plugins: { + test: { + rules: { + checker: context => ({ + Program() { + context.report({ loc: { line: 1, column: 1 }, message: "foo" }); + } + }) + } + } + }, + rules: { + "test/checker": "error" + } + }; + + const problems = linter.verify(code, config); + + assert.strictEqual(problems.length, 0); + }); + + it("rules should not change initial config", () => { + const config = { ...baseConfig, rules: { "test-plugin/test-rule": 2 } }; + const codeA = "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";"; + const codeB = "var a = \"trigger violation\";"; + let messages = linter.verify(codeA, config, filename, false); + + assert.strictEqual(messages.length, 0); + + messages = linter.verify(codeB, config, filename, false); + assert.strictEqual(messages.length, 1); + }); + }); + + describe("when evaluating code with comments to enable and disable all reporting", () => { + it("should report a violation", () => { + + const code = [ + "/*eslint-disable */", + "alert('test');", + "/*eslint-enable */", + "alert('test');" + ].join("\n"); + const config = { rules: { "no-alert": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].message, "Unexpected alert."); + assert.include(messages[0].nodeType, "CallExpression"); + assert.strictEqual(messages[0].line, 4); + }); + + it("should not report a violation", () => { + const code = [ + "/*eslint-disable */", + "alert('test');", + "alert('test');" + ].join("\n"); + const config = { rules: { "no-alert": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not report a violation", () => { + const code = [ + " alert('test1');/*eslint-disable */\n", + "alert('test');", + " alert('test');\n", + "/*eslint-enable */alert('test2');" + ].join(""); + const config = { rules: { "no-alert": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + assert.strictEqual(messages[0].column, 21); + assert.strictEqual(messages[1].column, 19); + }); + + it("should report a violation", () => { + + const code = [ + "/*eslint-disable */", + "alert('test');", + "/*eslint-disable */", + "alert('test');", + "/*eslint-enable*/", + "alert('test');", + "/*eslint-enable*/" + ].join("\n"); + + const config = { rules: { "no-alert": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + }); + + + it("should not report a violation", () => { + const code = [ + "/*eslint-disable */", + "(function(){ var b = 44;})()", + "/*eslint-enable */;any();" + ].join("\n"); + + const config = { rules: { "no-unused-vars": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not report a violation", () => { + const code = [ + "(function(){ /*eslint-disable */ var b = 44;})()", + "/*eslint-enable */;any();" + ].join("\n"); + + const config = { rules: { "no-unused-vars": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + }); + + describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => { + const code = "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');"; + + it("should report a violation", () => { + const config = { rules: { "no-console": 1, "no-alert": 0 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].message, "Unexpected alert."); + assert.include(messages[0].nodeType, "CallExpression"); + }); + }); + + describe("when evaluating code with comments to enable configurable rule", () => { + const code = "/*eslint quotes:[2, \"double\"]*/ alert('test');"; + + it("should report a violation", () => { + const config = { rules: { quotes: [2, "single"] } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "quotes"); + assert.strictEqual(messages[0].message, "Strings must use doublequote."); + assert.include(messages[0].nodeType, "Literal"); + }); + }); + + describe("when evaluating code with comments to enable configurable rule using string severity", () => { + const code = "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');"; + + it("should report a violation", () => { + const config = { rules: { quotes: [2, "single"] } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "quotes"); + assert.strictEqual(messages[0].message, "Strings must use doublequote."); + assert.include(messages[0].nodeType, "Literal"); + }); + }); + + describe("when evaluating code with incorrectly formatted comments to disable rule", () => { + it("should report a violation", () => { + const code = "/*eslint no-alert:'1'*/ alert('test');"; + + const config = { rules: { "no-alert": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + + /* + * Incorrectly formatted comment threw error; + * message from caught exception + * may differ amongst UAs, so verifying + * first part only as defined in the + * parseJsonConfig function in lib/eslint.js + */ + assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":'1'':/u); + assert.strictEqual(messages[0].line, 1); + assert.strictEqual(messages[0].column, 1); + + assert.strictEqual(messages[1].ruleId, "no-alert"); + assert.strictEqual(messages[1].message, "Unexpected alert."); + assert.include(messages[1].nodeType, "CallExpression"); + }); + + it("should report a violation", () => { + const code = "/*eslint no-alert:abc*/ alert('test');"; + + const config = { rules: { "no-alert": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + + /* + * Incorrectly formatted comment threw error; + * message from caught exception + * may differ amongst UAs, so verifying + * first part only as defined in the + * parseJsonConfig function in lib/eslint.js + */ + assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":abc':/u); + assert.strictEqual(messages[0].line, 1); + assert.strictEqual(messages[0].column, 1); + + assert.strictEqual(messages[1].ruleId, "no-alert"); + assert.strictEqual(messages[1].message, "Unexpected alert."); + assert.include(messages[1].nodeType, "CallExpression"); + }); + + it("should report a violation", () => { + const code = "/*eslint no-alert:0 2*/ alert('test');"; + + const config = { rules: { "no-alert": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + + /* + * Incorrectly formatted comment threw error; + * message from caught exception + * may differ amongst UAs, so verifying + * first part only as defined in the + * parseJsonConfig function in lib/eslint.js + */ + assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":0 2':/u); + assert.strictEqual(messages[0].line, 1); + assert.strictEqual(messages[0].column, 1); + + assert.strictEqual(messages[1].ruleId, "no-alert"); + assert.strictEqual(messages[1].message, "Unexpected alert."); + assert.include(messages[1].nodeType, "CallExpression"); + }); + }); + + describe("when evaluating code with comments which have colon in its value", () => { + const code = String.raw` +/* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */ +alert('test'); +`; + + it("should not parse errors, should report a violation", () => { + const messages = linter.verify(code, {}, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "max-len"); + assert.strictEqual(messages[0].message, "This line has a length of 129. Maximum allowed is 100."); + assert.include(messages[0].nodeType, "Program"); + }); + }); + + describe("when evaluating code with comments that contain escape sequences", () => { + const code = String.raw` +/* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */ +console.log("test"); +consolexlog("test2"); +var a = "test2"; +`; + + it("should validate correctly", () => { + const config = { rules: {} }; + const messages = linter.verify(code, config, filename); + const [message1, message2] = messages; + + assert.strictEqual(messages.length, 2); + assert.strictEqual(message1.ruleId, "max-len"); + assert.strictEqual(message1.message, "This line has a length of 21. Maximum allowed is 1."); + assert.strictEqual(message1.line, 4); + assert.strictEqual(message1.column, 1); + assert.include(message1.nodeType, "Program"); + assert.strictEqual(message2.ruleId, "max-len"); + assert.strictEqual(message2.message, "This line has a length of 16. Maximum allowed is 1."); + assert.strictEqual(message2.line, 5); + assert.strictEqual(message2.column, 1); + assert.include(message2.nodeType, "Program"); + }); + }); + + }); + + describe("/*eslint-disable*/ and /*eslint-enable*/", () => { + it("should report a violation", () => { + const code = [ + "/*eslint-disable no-alert */", + "alert('test');", + "console.log('test');" // here + ].join("\n"); + const config = { rules: { "no-alert": 1, "no-console": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + + assert.strictEqual(messages[0].ruleId, "no-console"); + }); + + it("should report no violation", () => { + const code = [ + "/* eslint-disable quotes */", + "console.log(\"foo\");", + "/* eslint-enable quotes */" + ].join("\n"); + const config = { rules: { quotes: 2 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should report a violation", () => { + const code = [ + "/*eslint-disable no-alert, no-console */", + "alert('test');", + "console.log('test');", + "/*eslint-enable*/", + + "alert('test');", // here + "console.log('test');" // here + ].join("\n"); + const config = { rules: { "no-alert": 1, "no-console": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].line, 5); + assert.strictEqual(messages[1].ruleId, "no-console"); + assert.strictEqual(messages[1].line, 6); + }); + + it("should report a violation", () => { + const code = [ + "/*eslint-disable no-alert */", + "alert('test');", + "console.log('test');", + "/*eslint-enable no-console */", + + "alert('test');" // here + ].join("\n"); + const config = { rules: { "no-alert": 1, "no-console": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + + assert.strictEqual(messages[0].ruleId, "no-console"); + }); + + + it("should report a violation", () => { + const code = [ + "/*eslint-disable no-alert, no-console */", + "alert('test');", + "console.log('test');", + "/*eslint-enable no-alert*/", + + "alert('test');", // here + "console.log('test');" + ].join("\n"); + const config = { rules: { "no-alert": 1, "no-console": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].line, 5); + }); + + + it("should report a violation", () => { + const code = [ + "/*eslint-disable no-alert */", + + "/*eslint-disable no-console */", + "alert('test');", + "console.log('test');", + "/*eslint-enable */", + + "alert('test');", // here + "console.log('test');", // here + + "/*eslint-enable */", + + "alert('test');", // here + "console.log('test');", // here + + "/*eslint-enable*/" + ].join("\n"); + const config = { rules: { "no-alert": 1, "no-console": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 4); + + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].line, 6); + + assert.strictEqual(messages[1].ruleId, "no-console"); + assert.strictEqual(messages[1].line, 7); + + assert.strictEqual(messages[2].ruleId, "no-alert"); + assert.strictEqual(messages[2].line, 9); + + assert.strictEqual(messages[3].ruleId, "no-console"); + assert.strictEqual(messages[3].line, 10); + + }); + + it("should report a violation", () => { + const code = [ + "/*eslint-disable no-alert, no-console */", + "alert('test');", + "console.log('test');", + + "/*eslint-enable no-alert */", + + "alert('test');", // here + "console.log('test');", + + "/*eslint-enable no-console */", + + "alert('test');", // here + "console.log('test');", // here + "/*eslint-enable no-console */" + ].join("\n"); + const config = { rules: { "no-alert": 1, "no-console": 1 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 3); + + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].line, 5); + + assert.strictEqual(messages[1].ruleId, "no-alert"); + assert.strictEqual(messages[1].line, 8); + + assert.strictEqual(messages[2].ruleId, "no-console"); + assert.strictEqual(messages[2].line, 9); + + }); + + it("should report a violation when severity is warn", () => { + const code = [ + "/*eslint-disable no-alert, no-console */", + "alert('test');", + "console.log('test');", + + "/*eslint-enable no-alert */", + + "alert('test');", // here + "console.log('test');", + + "/*eslint-enable no-console */", + + "alert('test');", // here + "console.log('test');", // here + "/*eslint-enable no-console */" + ].join("\n"); + const config = { rules: { "no-alert": "warn", "no-console": "warn" } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 3); + + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[0].line, 5); + + assert.strictEqual(messages[1].ruleId, "no-alert"); + assert.strictEqual(messages[1].line, 8); + + assert.strictEqual(messages[2].ruleId, "no-console"); + assert.strictEqual(messages[2].line, 9); + + }); + + it("should report no violation", () => { + const code = [ + "/*eslint-disable no-unused-vars */", + "var foo; // eslint-disable-line no-unused-vars", + "var bar;", + "/* eslint-enable no-unused-vars */" // here + ].join("\n"); + const config = { rules: { "no-unused-vars": 2 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + }); + + describe("/*eslint-disable-line*/", () => { + + it("should report a violation", () => { + const code = [ + "alert('test'); // eslint-disable-line no-alert", + "console.log('test');" // here + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + + assert.strictEqual(messages[0].ruleId, "no-console"); + }); + + it("should report a violation", () => { + const code = [ + "alert('test'); // eslint-disable-line no-alert", + "console.log('test'); // eslint-disable-line no-console", + "alert('test');" // here + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + + assert.strictEqual(messages[0].ruleId, "no-alert"); + }); + + it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => { + const code = [ + "/* eslint-disable-line", + "*", + "*/ console.log('test');" // here + ].join("\n"); + const config = { + rules: { + "no-console": 1 + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + + assert.strictEqual(messages[1].ruleId, "no-console"); + }); + + it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => { + const code = [ + "alert('test'); /* eslint-disable-line ", + "no-alert */" + ].join("\n"); + const config = { + rules: { + "no-alert": 1 + } + }; + + const messages = linter.verify(code, config); + + assert.deepStrictEqual(messages, [ + { + ruleId: "no-alert", + severity: 1, + line: 1, + column: 1, + endLine: 1, + endColumn: 14, + message: "Unexpected alert.", + messageId: "unexpected", + nodeType: "CallExpression" + }, + { + ruleId: null, + severity: 2, + message: "eslint-disable-line comment should not span multiple lines.", + line: 1, + column: 16, + endLine: 2, + endColumn: 12, + nodeType: null + } + ]); + }); + + it("should not report a violation for eslint-disable-line in block comment", () => { + const code = [ + "alert('test'); // eslint-disable-line no-alert", + "alert('test'); /*eslint-disable-line no-alert*/" + ].join("\n"); + const config = { + rules: { + "no-alert": 1 + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not report a violation", () => { + const code = [ + "alert('test'); // eslint-disable-line no-alert", + "console.log('test'); // eslint-disable-line no-console" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not report a violation", () => { + const code = [ + "alert('test') // eslint-disable-line no-alert, quotes, semi", + "console.log('test'); // eslint-disable-line" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + quotes: [1, "double"], + semi: [1, "always"], + "no-console": 1 + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not report a violation", () => { + const code = [ + "alert('test') /* eslint-disable-line no-alert, quotes, semi */", + "console.log('test'); /* eslint-disable-line */" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + quotes: [1, "double"], + semi: [1, "always"], + "no-console": 1 + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should ignore violations of multiple rules when specified in mixed comments", () => { + const code = [ + " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + quotes: [1, "single"] + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should report no violation", () => { + const code = [ + "var foo1; // eslint-disable-line no-unused-vars", + "var foo2; // eslint-disable-line no-unused-vars", + "var foo3; // eslint-disable-line no-unused-vars", + "var foo4; // eslint-disable-line no-unused-vars", + "var foo5; // eslint-disable-line no-unused-vars" + ].join("\n"); + const config = { rules: { "no-unused-vars": 2 } }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + }); + + describe("/*eslint-disable-next-line*/", () => { + it("should ignore violation of specified rule on next line", () => { + const code = [ + "// eslint-disable-next-line no-alert", + "alert('test');", + "console.log('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-console"); + }); + + it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => { + const code = [ + "/* eslint-disable-next-line no-alert */", + "alert('test');", + "console.log('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-console"); + }); + it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => { + const code = [ + "/* eslint-disable-next-line no-alert */", + "alert('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should not ignore violation if block comment is not on a single line", () => { + const code = [ + "/* eslint-disable-next-line", + "no-alert */alert('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + assert.strictEqual(messages[1].ruleId, "no-alert"); + }); + + it("should ignore violations only of specified rule", () => { + const code = [ + "// eslint-disable-next-line no-console", + "alert('test');", + "console.log('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[1].ruleId, "no-console"); + }); + + it("should ignore violations of multiple rules when specified", () => { + const code = [ + "// eslint-disable-next-line no-alert, quotes", + "alert(\"test\");", + "console.log('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + quotes: [1, "single"], + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-console"); + }); + + it("should ignore violations of multiple rules when specified in mixed comments", () => { + const code = [ + "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes", + "alert(\"test\");" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + quotes: [1, "single"] + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 0); + }); + + it("should ignore violations of only the specified rule on next line", () => { + const code = [ + "// eslint-disable-next-line quotes", + "alert(\"test\");", + "console.log('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + quotes: [1, "single"], + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[1].ruleId, "no-console"); + }); + + it("should ignore violations of specified rule on next line only", () => { + const code = [ + "alert('test');", + "// eslint-disable-next-line no-alert", + "alert('test');", + "console.log('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[1].ruleId, "no-console"); + }); + + it("should ignore all rule violations on next line if none specified", () => { + const code = [ + "// eslint-disable-next-line", + "alert(\"test\");", + "console.log('test')" + ].join("\n"); + const config = { + rules: { + semi: [1, "never"], + quotes: [1, "single"], + "no-alert": 1, + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-console"); + }); + + it("should ignore violations if eslint-disable-next-line is a block comment", () => { + const code = [ + "alert('test');", + "/* eslint-disable-next-line no-alert */", + "alert('test');", + "console.log('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[1].ruleId, "no-console"); + }); + + it("should report a violation", () => { + const code = [ + "/* eslint-disable-next-line", + "*", + "*/", + "console.log('test');" // here + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + + assert.strictEqual(messages[1].ruleId, "no-console"); + }); + + it("should not ignore violations if comment is of the type hashbang", () => { + const code = [ + "#! eslint-disable-next-line no-alert", + "alert('test');", + "console.log('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1, + "no-console": 1 + } + }; + const messages = linter.verify(code, config, filename); + + assert.strictEqual(messages.length, 2); + assert.strictEqual(messages[0].ruleId, "no-alert"); + assert.strictEqual(messages[1].ruleId, "no-console"); + }); + }); + + describe("descriptions in directive comments", () => { + it("should ignore the part preceded by '--' in '/*eslint*/'.", () => { + const aaa = sinon.stub().returns({}); + const bbb = sinon.stub().returns({}); + const config = { + plugins: { + test: { + rules: { + aaa: { create: aaa }, + bbb: { create: bbb } + } + } + } + }; + + const messages = linter.verify(` + /*eslint test/aaa:error -- test/bbb:error */ + console.log("hello") + `, config); + + // Don't include syntax error of the comment. + assert.deepStrictEqual(messages, []); + + // Use only `aaa`. + assert.strictEqual(aaa.callCount, 1); + assert.strictEqual(bbb.callCount, 0); + }); + + it("should ignore the part preceded by '--' in '/*globals*/'.", () => { + const messages = linter.verify(` + /*globals aaa -- bbb */ + var aaa = {} + var bbb = {} + `, { + languageOptions: { + sourceType: "script" + }, + rules: { "no-redeclare": "error" } + }); + + // Don't include `bbb` + assert.deepStrictEqual( + messages, + [{ + column: 31, + endColumn: 34, + line: 2, + endLine: 2, + message: "'aaa' is already defined by a variable declaration.", + messageId: "redeclaredBySyntax", + nodeType: "Block", + ruleId: "no-redeclare", + severity: 2 + }] + ); + }); + + it("should ignore the part preceded by '--' in '/*exported*/'.", () => { + const messages = linter.verify(` + /*exported aaa -- bbb */ + var aaa = {} + var bbb = {} + `, { + languageOptions: { + sourceType: "script" + }, + rules: { "no-unused-vars": "error" } + }); + + // Don't include `aaa` + assert.deepStrictEqual( + messages, + [{ + column: 25, + endColumn: 28, + endLine: 4, + line: 4, + message: "'bbb' is assigned a value but never used.", + messageId: "unusedVar", + nodeType: "Identifier", + ruleId: "no-unused-vars", + severity: 2 + }] + ); + }); + + it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => { + const messages = linter.verify(` + /*eslint-disable no-redeclare -- no-unused-vars */ + var aaa = {} + var aaa = {} + `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } }); + + // Do include `no-unused-vars` but not `no-redeclare` + assert.deepStrictEqual( + messages, + [{ + column: 25, + endLine: 4, + endColumn: 28, + line: 4, + message: "'aaa' is assigned a value but never used.", + messageId: "unusedVar", + nodeType: "Identifier", + ruleId: "no-unused-vars", + severity: 2 + }] + ); + }); + + it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => { + const messages = linter.verify(` + /*eslint-disable no-redeclare, no-unused-vars */ + /*eslint-enable no-redeclare -- no-unused-vars */ + var aaa = {} + var aaa = {} + `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } }); + + // Do include `no-redeclare` but not `no-unused-vars` + assert.deepStrictEqual( + messages, + [{ + column: 25, + endLine: 5, + endColumn: 28, + line: 5, + message: "'aaa' is already defined.", + messageId: "redeclared", + nodeType: "Identifier", + ruleId: "no-redeclare", + severity: 2 + }] + ); + }); + + it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => { + const messages = linter.verify(` + var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars + var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars + `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } }); + + // Do include `no-unused-vars` but not `no-redeclare` + assert.deepStrictEqual( + messages, + [{ + column: 25, + endLine: 3, + endColumn: 28, + line: 3, + message: "'aaa' is assigned a value but never used.", + messageId: "unusedVar", + nodeType: "Identifier", + ruleId: "no-unused-vars", + severity: 2 + }] + ); + }); + + it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => { + const messages = linter.verify(` + var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */ + var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */ + `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } }); + + // Do include `no-unused-vars` but not `no-redeclare` + assert.deepStrictEqual( + messages, + [{ + column: 25, + endLine: 3, + endColumn: 28, + line: 3, + message: "'aaa' is assigned a value but never used.", + messageId: "unusedVar", + nodeType: "Identifier", + ruleId: "no-unused-vars", + severity: 2 + }] + ); + }); + + it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => { + const messages = linter.verify(` + //eslint-disable-next-line no-redeclare -- no-unused-vars + var aaa = {} + //eslint-disable-next-line no-redeclare -- no-unused-vars + var aaa = {} + `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } }); + + // Do include `no-unused-vars` but not `no-redeclare` + assert.deepStrictEqual( + messages, + [{ + column: 25, + endLine: 5, + endColumn: 28, + line: 5, + message: "'aaa' is assigned a value but never used.", + messageId: "unusedVar", + nodeType: "Identifier", + ruleId: "no-unused-vars", + severity: 2 + }] + ); + }); + + it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => { + const messages = linter.verify(` + /*eslint-disable-next-line no-redeclare -- no-unused-vars */ + var aaa = {} + /*eslint-disable-next-line no-redeclare -- no-unused-vars */ + var aaa = {} + `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } }); + + // Do include `no-unused-vars` but not `no-redeclare` + assert.deepStrictEqual( + messages, + [{ + column: 25, + endLine: 5, + endColumn: 28, + line: 5, + message: "'aaa' is assigned a value but never used.", + messageId: "unusedVar", + nodeType: "Identifier", + ruleId: "no-unused-vars", + severity: 2 + }] + ); + }); + + it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => { + const rule = sinon.stub().returns({}); + const config = { + plugins: { + test: { + rules: { + "a--rule": { create: rule } + } + } + } + }; + + const messages = linter.verify(` + /*eslint test/a--rule:error */ + console.log("hello") + `, config); + + // Don't include syntax error of the comment. + assert.deepStrictEqual(messages, []); + + // Use `a--rule`. + assert.strictEqual(rule.callCount, 1); + }); + + it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => { + const aaa = sinon.stub().returns({}); + const bbb = sinon.stub().returns({}); + const config = { + plugins: { + test: { + rules: { + aaa: { create: aaa }, + bbb: { create: bbb } + } + } + } + }; + + const messages = linter.verify(` + /*eslint test/aaa:error -------- test/bbb:error */ + console.log("hello") + `, config); + + // Don't include syntax error of the comment. + assert.deepStrictEqual(messages, []); + + // Use only `aaa`. + assert.strictEqual(aaa.callCount, 1); + assert.strictEqual(bbb.callCount, 0); + }); + + it("should ignore the part preceded by '--' with line breaks.", () => { + const aaa = sinon.stub().returns({}); + const bbb = sinon.stub().returns({}); + const config = { + plugins: { + test: { + rules: { + aaa: { create: aaa }, + bbb: { create: bbb } + } + } + } + }; + + const messages = linter.verify(` + /*eslint test/aaa:error + -------- + test/bbb:error */ + console.log("hello") + `, config); + + // Don't include syntax error of the comment. + assert.deepStrictEqual(messages, []); + + // Use only `aaa`. + assert.strictEqual(aaa.callCount, 1); + assert.strictEqual(bbb.callCount, 0); + }); + }); + + describe("allowInlineConfig option", () => { + describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => { + it("should report a violation for disabling rules", () => { + const code = [ + "alert('test'); // eslint-disable-line no-alert" + ].join("\n"); + const config = { + rules: { + "no-alert": 1 + } + }; + + const messages = linter.verify(code, config, { + filename, + allowInlineConfig: false + }); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-alert"); + }); + + it("should report a violation for global variable declarations", () => { + let ok = false; + const code = [ + "/* global foo */" + ].join("\n"); + const config = { + plugins: { + test: { + rules: { + test(context) { + return { + Program() { + const scope = context.getScope(); + const sourceCode = context.getSourceCode(); + const comments = sourceCode.getAllComments(); + + assert.strictEqual(1, comments.length); + + const foo = getVariable(scope, "foo"); + + assert.notOk(foo); + + ok = true; + } + }; + } + } + } + }, + rules: { + "test/test": 2 + } + }; + + linter.verify(code, config, { allowInlineConfig: false }); + assert(ok); + }); + + it("should report a violation for eslint-disable", () => { + const code = [ + "/* eslint-disable */", + "alert('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 1 + } + }; + + const messages = linter.verify(code, config, { + filename, + allowInlineConfig: false + }); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-alert"); + }); + + it("should not report a violation for rule changes", () => { + const code = [ + "/*eslint no-alert:2*/", + "alert('test');" + ].join("\n"); + const config = { + rules: { + "no-alert": 0 + } + }; + + const messages = linter.verify(code, config, { + filename, + allowInlineConfig: false + }); + + assert.strictEqual(messages.length, 0); + }); + + it("should report a violation for disable-line", () => { + const code = [ + "alert('test'); // eslint-disable-line" + ].join("\n"); + const config = { + rules: { + "no-alert": 2 + } + }; + + const messages = linter.verify(code, config, { + filename, + allowInlineConfig: false + }); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-alert"); + }); + + }); + + describe("when evaluating code with 'noInlineConfig'", () => { + for (const directive of [ + "globals foo", + "global foo", + "exported foo", + "eslint eqeqeq: error", + "eslint-disable eqeqeq", + "eslint-disable-line eqeqeq", + "eslint-disable-next-line eqeqeq", + "eslint-enable eqeqeq" + ]) { + // eslint-disable-next-line no-loop-func -- No closures + it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => { + const messages = linter.verify(`/* ${directive} */`, { + linterOptions: { + noInlineConfig: true + } + }); + + assert.deepStrictEqual(messages.length, 1); + assert.deepStrictEqual(messages[0].fatal, void 0); + assert.deepStrictEqual(messages[0].ruleId, null); + assert.deepStrictEqual(messages[0].severity, 1); + assert.deepStrictEqual(messages[0].message, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`); + }); + } + + for (const directive of [ + "eslint-disable-line eqeqeq", + "eslint-disable-next-line eqeqeq" + ]) { + // eslint-disable-next-line no-loop-func -- No closures + it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => { + const messages = linter.verify(`// ${directive}`, { + linterOptions: { + noInlineConfig: true + } + }); + + assert.deepStrictEqual(messages.length, 1); + assert.deepStrictEqual(messages[0].fatal, void 0); + assert.deepStrictEqual(messages[0].ruleId, null); + assert.deepStrictEqual(messages[0].severity, 1); + assert.deepStrictEqual(messages[0].message, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`); + }); + } + + it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => { + const messages = linter.verify("/* globals foo */", { + linterOptions: { + noInlineConfig: true + } + }, { allowInlineConfig: false }); + + assert.deepStrictEqual(messages.length, 0); + }); + }); + + describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => { + it("should not report a violation", () => { + const code = [ + "alert('test'); // eslint-disable-line no-alert" + ].join("\n"); + const config = { + rules: { + "no-alert": 1 + } + }; + + const messages = linter.verify(code, config, { + filename, + allowInlineConfig: true + }); + + assert.strictEqual(messages.length, 0); + }); + }); + + }); + + describe("reportUnusedDisableDirectives option", () => { + it("reports problems for unused eslint-disable comments", () => { + assert.deepStrictEqual( + linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: true }), + [ + { + ruleId: null, + message: "Unused eslint-disable directive (no problems were reported).", + line: 1, + column: 1, + fix: { + range: [0, 20], + text: " " + }, + severity: 2, + nodeType: null + } + ] + ); + }); + + it("reports problems for unused eslint-disable comments (error)", () => { + assert.deepStrictEqual( + linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "error" }), + [ + { + ruleId: null, + message: "Unused eslint-disable directive (no problems were reported).", + line: 1, + column: 1, + fix: { + range: [0, 20], + text: " " + }, + severity: 2, + nodeType: null + } + ] + ); + }); + + it("reports problems for unused eslint-disable comments (warn)", () => { + assert.deepStrictEqual( + linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "warn" }), + [ + { + ruleId: null, + message: "Unused eslint-disable directive (no problems were reported).", + line: 1, + column: 1, + fix: { + range: [0, 20], + text: " " + }, + severity: 1, + nodeType: null + } + ] + ); + }); + + it("reports problems for unused eslint-disable comments (in config)", () => { + assert.deepStrictEqual( + linter.verify("/* eslint-disable */", { + linterOptions: { + reportUnusedDisableDirectives: true + } + }), + [ + { + ruleId: null, + message: "Unused eslint-disable directive (no problems were reported).", + line: 1, + column: 1, + fix: { + range: [0, 20], + text: " " + }, + severity: 1, + nodeType: null + } + ] + ); + }); + + it("reports problems for partially unused eslint-disable comments (in config)", () => { + const code = "alert('test'); // eslint-disable-line no-alert, no-redeclare"; + const config = { + linterOptions: { + reportUnusedDisableDirectives: true + }, + rules: { + "no-alert": 1, + "no-redeclare": 1 + } + }; + + const messages = linter.verify(code, config, { + filename, + allowInlineConfig: true + }); + + assert.deepStrictEqual( + messages, + [ + { + ruleId: null, + message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').", + line: 1, + column: 16, + fix: { + range: [46, 60], + text: "" + }, + severity: 1, + nodeType: null + } + ] + ); + }); + + describe("autofix", () => { + const alwaysReportsRule = { + create(context) { + return { + Program(node) { + context.report({ message: "bad code", loc: node.loc.end }); + } + }; + } + }; + + const neverReportsRule = { + create() { + return {}; + } + }; + + const ruleCount = 3; + const usedRules = Array.from( + { length: ruleCount }, + (_, index) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2" + ); + const unusedRules = usedRules.map(name => `un${name}`); // "unused", "unused-1", "unused-2" + + const config = { + plugins: { + test: { + rules: {} + } + }, + linterOptions: { + reportUnusedDisableDirectives: true + }, + rules: { + ...Object.fromEntries(usedRules.map(name => [`test/${name}`, "error"])), + ...Object.fromEntries(unusedRules.map(name => [`test/${name}`, "error"])) + } + }; + + beforeEach(() => { + config.plugins.test.rules = { + ...Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])), + ...Object.fromEntries(unusedRules.map(name => [name, neverReportsRule])) + }; + }); + + const tests = [ + + //----------------------------------------------- + // Removing the entire comment + //----------------------------------------------- + + { + code: "// eslint-disable-line test/unused", + output: " " + }, + { + code: "foo// eslint-disable-line test/unused", + output: "foo " + }, + { + code: "// eslint-disable-line ,test/unused,", + output: " " + }, + { + code: "// eslint-disable-line test/unused-1, test/unused-2", + output: " " + }, + { + code: "// eslint-disable-line ,test/unused-1,, test/unused-2,, -- comment", + output: " " + }, + { + code: "// eslint-disable-next-line test/unused\n", + output: " \n" + }, + { + code: "// eslint-disable-next-line test/unused\nfoo", + output: " \nfoo" + }, + { + code: "/* eslint-disable \ntest/unused\n*/", + output: " " + }, + + //----------------------------------------------- + // Removing only individual rules + //----------------------------------------------- + + // content before the first rule should not be changed + { + code: "//eslint-disable-line test/unused, test/used", + output: "//eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused, test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused, test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "/*\neslint-disable test/unused, test/used*/", + output: "/*\neslint-disable test/used*/" + }, + { + code: "/*\n eslint-disable test/unused, test/used*/", + output: "/*\n eslint-disable test/used*/" + }, + { + code: "/*\r\neslint-disable test/unused, test/used*/", + output: "/*\r\neslint-disable test/used*/" + }, + { + code: "/*\u2028eslint-disable test/unused, test/used*/", + output: "/*\u2028eslint-disable test/used*/" + }, + { + code: "/*\u00A0eslint-disable test/unused, test/used*/", + output: "/*\u00A0eslint-disable test/used*/" + }, + { + code: "// eslint-disable-line test/unused, test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "/* eslint-disable\ntest/unused, test/used*/", + output: "/* eslint-disable\ntest/used*/" + }, + { + code: "/* eslint-disable\n test/unused, test/used*/", + output: "/* eslint-disable\n test/used*/" + }, + { + code: "/* eslint-disable\r\ntest/unused, test/used*/", + output: "/* eslint-disable\r\ntest/used*/" + }, + { + code: "/* eslint-disable\u2028test/unused, test/used*/", + output: "/* eslint-disable\u2028test/used*/" + }, + { + code: "/* eslint-disable\u00A0test/unused, test/used*/", + output: "/* eslint-disable\u00A0test/used*/" + }, + + // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed + { + code: "// eslint-disable-line test/unused,test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused, test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused , test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused, test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused ,test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "/* eslint-disable test/unused\n,\ntest/used */", + output: "/* eslint-disable test/used */" + }, + { + code: "/* eslint-disable test/unused \n \n,\n\n test/used */", + output: "/* eslint-disable test/used */" + }, + { + code: "/* eslint-disable test/unused\u2028,\u2028test/used */", + output: "/* eslint-disable test/used */" + }, + { + code: "// eslint-disable-line test/unused\u00A0,\u00A0test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused,,test/used", + output: "// eslint-disable-line ,test/used" + }, + { + code: "// eslint-disable-line test/unused, ,test/used", + output: "// eslint-disable-line ,test/used" + }, + { + code: "// eslint-disable-line test/unused,, test/used", + output: "// eslint-disable-line , test/used" + }, + { + code: "// eslint-disable-line test/unused,test/used ", + output: "// eslint-disable-line test/used " + }, + { + code: "// eslint-disable-next-line test/unused,test/used\n", + output: "// eslint-disable-next-line test/used\n" + }, + + // when removing a rule in the middle, one comma and all whitespace between commas should also be removed + { + code: "// eslint-disable-line test/used-1,test/unused,test/used-2", + output: "// eslint-disable-line test/used-1,test/used-2" + }, + { + code: "// eslint-disable-line test/used-1, test/unused,test/used-2", + output: "// eslint-disable-line test/used-1,test/used-2" + }, + { + code: "// eslint-disable-line test/used-1,test/unused ,test/used-2", + output: "// eslint-disable-line test/used-1,test/used-2" + }, + { + code: "// eslint-disable-line test/used-1, test/unused ,test/used-2", + output: "// eslint-disable-line test/used-1,test/used-2" + }, + { + code: "/* eslint-disable test/used-1,\ntest/unused\n,test/used-2 */", + output: "/* eslint-disable test/used-1,test/used-2 */" + }, + { + code: "/* eslint-disable test/used-1,\n\n test/unused \n \n ,test/used-2 */", + output: "/* eslint-disable test/used-1,test/used-2 */" + }, + { + code: "/* eslint-disable test/used-1,\u2028test/unused\u2028,test/used-2 */", + output: "/* eslint-disable test/used-1,test/used-2 */" + }, + { + code: "// eslint-disable-line test/used-1,\u00A0test/unused\u00A0,test/used-2", + output: "// eslint-disable-line test/used-1,test/used-2" + }, + + // when removing a rule in the middle, content around commas should not be changed + { + code: "// eslint-disable-line test/used-1, test/unused ,test/used-2", + output: "// eslint-disable-line test/used-1,test/used-2" + }, + { + code: "// eslint-disable-line test/used-1,test/unused, test/used-2", + output: "// eslint-disable-line test/used-1, test/used-2" + }, + { + code: "// eslint-disable-line test/used-1 ,test/unused,test/used-2", + output: "// eslint-disable-line test/used-1 ,test/used-2" + }, + { + code: "// eslint-disable-line test/used-1 ,test/unused, test/used-2", + output: "// eslint-disable-line test/used-1 , test/used-2" + }, + { + code: "// eslint-disable-line test/used-1 , test/unused , test/used-2", + output: "// eslint-disable-line test/used-1 , test/used-2" + }, + { + code: "/* eslint-disable test/used-1\n,test/unused,\ntest/used-2 */", + output: "/* eslint-disable test/used-1\n,\ntest/used-2 */" + }, + { + code: "/* eslint-disable test/used-1\u2028,test/unused,\u2028test/used-2 */", + output: "/* eslint-disable test/used-1\u2028,\u2028test/used-2 */" + }, + { + code: "// eslint-disable-line test/used-1\u00A0,test/unused,\u00A0test/used-2", + output: "// eslint-disable-line test/used-1\u00A0,\u00A0test/used-2" + }, + { + code: "// eslint-disable-line , test/unused ,test/used", + output: "// eslint-disable-line ,test/used" + }, + { + code: "/* eslint-disable\n, test/unused ,test/used */", + output: "/* eslint-disable\n,test/used */" + }, + { + code: "/* eslint-disable test/used-1,\n,test/unused,test/used-2 */", + output: "/* eslint-disable test/used-1,\n,test/used-2 */" + }, + { + code: "/* eslint-disable test/used-1,test/unused,\n,test/used-2 */", + output: "/* eslint-disable test/used-1,\n,test/used-2 */" + }, + { + code: "/* eslint-disable test/used-1,\n,test/unused,\n,test/used-2 */", + output: "/* eslint-disable test/used-1,\n,\n,test/used-2 */" + }, + { + code: "// eslint-disable-line test/used, test/unused,", + output: "// eslint-disable-line test/used," + }, + { + code: "// eslint-disable-next-line test/used, test/unused,\n", + output: "// eslint-disable-next-line test/used,\n" + }, + { + code: "// eslint-disable-line test/used, test/unused, ", + output: "// eslint-disable-line test/used, " + }, + { + code: "// eslint-disable-line test/used, test/unused, -- comment", + output: "// eslint-disable-line test/used, -- comment" + }, + { + code: "/* eslint-disable test/used, test/unused,\n*/", + output: "/* eslint-disable test/used,\n*/" + }, + + // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed + { + code: "// eslint-disable-line test/used,test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used, test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used ,test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used , test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used, test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used ,test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "/* eslint-disable test/used\n,\ntest/unused */", + output: "/* eslint-disable test/used */" + }, + { + code: "/* eslint-disable test/used \n \n,\n\n test/unused */", + output: "/* eslint-disable test/used */" + }, + { + code: "/* eslint-disable test/used\u2028,\u2028test/unused */", + output: "/* eslint-disable test/used */" + }, + { + code: "// eslint-disable-line test/used\u00A0,\u00A0test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used,,test/unused", + output: "// eslint-disable-line test/used," + }, + { + code: "// eslint-disable-line test/used, ,test/unused", + output: "// eslint-disable-line test/used," + }, + { + code: "/* eslint-disable test/used,\n,test/unused */", + output: "/* eslint-disable test/used, */" + }, + { + code: "/* eslint-disable test/used\n, ,test/unused */", + output: "/* eslint-disable test/used\n, */" + }, + + // content after the last rule should not be changed + { + code: "// eslint-disable-line test/used,test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used,test/unused ", + output: "// eslint-disable-line test/used " + }, + { + code: "// eslint-disable-line test/used,test/unused ", + output: "// eslint-disable-line test/used " + }, + { + code: "// eslint-disable-line test/used,test/unused -- comment", + output: "// eslint-disable-line test/used -- comment" + }, + { + code: "// eslint-disable-next-line test/used,test/unused\n", + output: "// eslint-disable-next-line test/used\n" + }, + { + code: "// eslint-disable-next-line test/used,test/unused \n", + output: "// eslint-disable-next-line test/used \n" + }, + { + code: "/* eslint-disable test/used,test/unused\u2028*/", + output: "/* eslint-disable test/used\u2028*/" + }, + { + code: "// eslint-disable-line test/used,test/unused\u00A0", + output: "// eslint-disable-line test/used\u00A0" + }, + + // multiply rules to remove + { + code: "// eslint-disable-line test/used, test/unused-1, test/unused-2", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused-1, test/used, test/unused-2", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused-1, test/unused-2, test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used-1, test/unused-1, test/used-2, test/unused-2", + output: "// eslint-disable-line test/used-1, test/used-2" + }, + { + code: "// eslint-disable-line test/unused-1, test/used-1, test/unused-2, test/used-2", + output: "// eslint-disable-line test/used-1, test/used-2" + }, + { + code: ` + /* eslint-disable test/unused-1, + test/used-1, + test/unused-2, + test/used-2 + */ + `, + output: ` + /* eslint-disable test/used-1, + test/used-2 + */ + ` + }, + { + code: ` + /* eslint-disable + test/unused-1, + test/used-1, + test/unused-2, + test/used-2 + */ + `, + output: ` + /* eslint-disable + test/used-1, + test/used-2 + */ + ` + }, + { + code: ` + /* eslint-disable + test/used-1, + test/unused-1, + test/used-2, + test/unused-2 + */ + `, + output: ` + /* eslint-disable + test/used-1, + test/used-2 + */ + ` + }, + { + code: ` + /* eslint-disable + test/used-1, + test/unused-1, + test/used-2, + test/unused-2, + */ + `, + output: ` + /* eslint-disable + test/used-1, + test/used-2, + */ + ` + }, + { + code: ` + /* eslint-disable + ,test/unused-1 + ,test/used-1 + ,test/unused-2 + ,test/used-2 + */ + `, + output: ` + /* eslint-disable + ,test/used-1 + ,test/used-2 + */ + ` + }, + { + code: ` + /* eslint-disable + ,test/used-1 + ,test/unused-1 + ,test/used-2 + ,test/unused-2 + */ + `, + output: ` + /* eslint-disable + ,test/used-1 + ,test/used-2 + */ + ` + }, + { + code: ` + /* eslint-disable + test/used-1, + test/unused-1, + test/used-2, + test/unused-2 + + -- comment + */ + `, + output: ` + /* eslint-disable + test/used-1, + test/used-2 + + -- comment + */ + ` + }, + + // duplicates in the list + { + code: "// eslint-disable-line test/unused, test/unused, test/used", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/unused, test/used, test/unused", + output: "// eslint-disable-line test/used" + }, + { + code: "// eslint-disable-line test/used, test/unused, test/unused, test/used", + output: "// eslint-disable-line test/used, test/used" + } + ]; + + for (const { code, output } of tests) { + // eslint-disable-next-line no-loop-func -- `linter` is getting updated in beforeEach() + it(code, () => { + assert.strictEqual( + linter.verifyAndFix(code, config).output, + output + ); + }); + } + }); + }); + + }); + + describe("Default Global Variables", () => { + const code = "x"; + + it("builtin global variables should be available in the global scope", () => { + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.notStrictEqual(getVariable(scope, "Object"), null); + assert.notStrictEqual(getVariable(scope, "Array"), null); + assert.notStrictEqual(getVariable(scope, "undefined"), null); + }); + + return { Program: spy }; + } + } + } + }, + languageOptions: { + ecmaVersion: 5, + sourceType: "script" + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce, "Rule should have been called."); + }); + + it("ES6 global variables should be available by default", () => { + let spy; + const config = { + plugins: { + test: { + rules: { + checker: context => { + spy = sinon.spy(() => { + const scope = context.getScope(); + + assert.notStrictEqual(getVariable(scope, "Promise"), null); + assert.notStrictEqual(getVariable(scope, "Symbol"), null); + assert.notStrictEqual(getVariable(scope, "WeakMap"), null); + }); + + return { Program: spy }; + } + } + } + }, + languageOptions: { + sourceType: "script" + }, + rules: { + "test/checker": "error" + } + }; + + linter.verify(code, config); + assert(spy && spy.calledOnce); + }); + + }); + + describe("Suggestions", () => { + it("provides suggestion information for tools to use", () => { + + const config = { + plugins: { + test: { + rules: { + "rule-with-suggestions": { + meta: { hasSuggestions: true }, + create: context => ({ + Program(node) { + context.report({ + node, + message: "Incorrect spacing", + suggest: [{ + desc: "Insert space at the beginning", + fix: fixer => fixer.insertTextBefore(node, " ") + }, { + desc: "Insert space at the end", + fix: fixer => fixer.insertTextAfter(node, " ") + }] + }); + } + }) + } + } + } + }, + rules: { + "test/rule-with-suggestions": "error" + } + }; + + const messages = linter.verify("var a = 1;", config); + + assert.deepStrictEqual(messages[0].suggestions, [{ + desc: "Insert space at the beginning", + fix: { + range: [0, 0], + text: " " + } + }, { + desc: "Insert space at the end", + fix: { + range: [10, 10], + text: " " + } + }]); + }); + + it("supports messageIds for suggestions", () => { + + const config = { + plugins: { + test: { + rules: { + "rule-with-suggestions": { + meta: { + messages: { + suggestion1: "Insert space at the beginning", + suggestion2: "Insert space at the end" + }, + hasSuggestions: true + }, + create: context => ({ + Program(node) { + context.report({ + node, + message: "Incorrect spacing", + suggest: [{ + messageId: "suggestion1", + fix: fixer => fixer.insertTextBefore(node, " ") + }, { + messageId: "suggestion2", + fix: fixer => fixer.insertTextAfter(node, " ") + }] + }); + } + }) + } + } + } + }, + rules: { + "test/rule-with-suggestions": "error" + } + }; + + const messages = linter.verify("var a = 1;", config); + + assert.deepStrictEqual(messages[0].suggestions, [{ + messageId: "suggestion1", + desc: "Insert space at the beginning", + fix: { + range: [0, 0], + text: " " + } + }, { + messageId: "suggestion2", + desc: "Insert space at the end", + fix: { + range: [10, 10], + text: " " + } + }]); + }); + + it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled", () => { + + const config = { + plugins: { + test: { + rules: { + "rule-with-suggestions": { + meta: { docs: {}, schema: [] }, + create: context => ({ + Program(node) { + context.report({ + node, + message: "hello world", + suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }] + }); + } + }) + } + } + } + }, + rules: { + "test/rule-with-suggestions": "error" + } + }; + + assert.throws(() => { + linter.verify("0", config); + }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`."); + }); + + it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled and the rule has the obsolete `meta.docs.suggestion` property", () => { + + const config = { + plugins: { + test: { + rules: { + "rule-with-meta-docs-suggestion": { + meta: { docs: { suggestion: true }, schema: [] }, + create: context => ({ + Program(node) { + context.report({ + node, + message: "hello world", + suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }] + }); + } + }) + } + } + } + }, + rules: { + "test/rule-with-meta-docs-suggestion": "error" + } + }; + + assert.throws(() => { + linter.verify("0", config); + }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`. `meta.docs.suggestion` is ignored by ESLint."); + }); + }); + + + describe("Error Conditions", () => { + describe("when evaluating broken code", () => { + const code = BROKEN_TEST_CODE; + + it("should report a violation with a useful parse error prefix", () => { + const messages = linter.verify(code); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.isNull(messages[0].ruleId); + assert.strictEqual(messages[0].line, 1); + assert.strictEqual(messages[0].column, 4); + assert.isTrue(messages[0].fatal); + assert.match(messages[0].message, /^Parsing error:/u); + }); + + it("should report source code where the issue is present", () => { + const inValidCode = [ + "var x = 20;", + "if (x ==4 {", + " x++;", + "}" + ]; + const messages = linter.verify(inValidCode.join("\n")); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.isTrue(messages[0].fatal); + assert.match(messages[0].message, /^Parsing error:/u); + }); + }); + + describe("when using a rule which has been replaced", () => { + const code = TEST_CODE; + + it("should report the new rule", () => { + + assert.throws(() => { + linter.verify(code, { rules: { "no-comma-dangle": 2 } }); + }, /Key "rules": Key "no-comma-dangle": Rule "no-comma-dangle" was removed and replaced by "comma-dangle"/u); + + }); + }); + + }); + }); + + describe("getSourceCode()", () => { + const code = TEST_CODE; + + it("should retrieve SourceCode object after reset", () => { + linter.verify(code, {}, filename, true); + + const sourceCode = linter.getSourceCode(); + + assert.isObject(sourceCode); + assert.strictEqual(sourceCode.text, code); + assert.isObject(sourceCode.ast); + }); + + it("should retrieve SourceCode object without reset", () => { + linter.verify(code, {}, filename); + + const sourceCode = linter.getSourceCode(); + + assert.isObject(sourceCode); + assert.strictEqual(sourceCode.text, code); + assert.isObject(sourceCode.ast); + }); + + }); + + describe("defineRule()", () => { + it("should throw an error when called in flat config mode", () => { + assert.throws(() => { + linter.defineRule("foo", () => {}); + }, /This method cannot be used with flat config/u); + }); + }); + + describe("defineRules()", () => { + it("should throw an error when called in flat config mode", () => { + assert.throws(() => { + linter.defineRules({}); + }, /This method cannot be used with flat config/u); + }); + }); + + describe("defineParser()", () => { + it("should throw an error when called in flat config mode", () => { + assert.throws(() => { + linter.defineParser("foo", {}); + }, /This method cannot be used with flat config/u); + }); + }); + + describe("getRules()", () => { + it("should throw an error when called in flat config mode", () => { + assert.throws(() => { + linter.getRules(); + }, /This method cannot be used with flat config/u); + }); + }); + + describe("version", () => { + it("should return current version number", () => { + const version = linter.version; + + assert.isString(version); + assert.isTrue(parseInt(version[0], 10) >= 3); + }); + }); + + describe("verifyAndFix()", () => { + it("Fixes the code", () => { + const messages = linter.verifyAndFix("var a", { + rules: { + semi: 2 + } + }, { filename: "test.js" }); + + assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly"); + assert.isTrue(messages.fixed); + }); + + it("does not require a third argument", () => { + const fixResult = linter.verifyAndFix("var a", { + rules: { + semi: 2 + } + }); + + assert.deepStrictEqual(fixResult, { + fixed: true, + messages: [], + output: "var a;" + }); + }); + + it("does not include suggestions in autofix results", () => { + const fixResult = linter.verifyAndFix("var foo = /\\#/", { + rules: { + semi: 2, + "no-useless-escape": 2 + } + }); + + assert.strictEqual(fixResult.output, "var foo = /\\#/;"); + assert.strictEqual(fixResult.fixed, true); + assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true); + }); + + it("does not apply autofixes when fix argument is `false`", () => { + const fixResult = linter.verifyAndFix("var a", { + rules: { + semi: 2 + } + }, { fix: false }); + + assert.strictEqual(fixResult.fixed, false); + }); + + it("stops fixing after 10 passes", () => { + + const config = { + plugins: { + test: { + rules: { + "add-spaces": { + meta: { + fixable: "whitespace" + }, + create(context) { + return { + Program(node) { + context.report({ + node, + message: "Add a space before this node.", + fix: fixer => fixer.insertTextBefore(node, " ") + }); + } + }; + } + } + } + } + }, + rules: { + "test/add-spaces": "error" + } + }; + + const fixResult = linter.verifyAndFix("a", config); + + assert.strictEqual(fixResult.fixed, true); + assert.strictEqual(fixResult.output, `${" ".repeat(10)}a`); + assert.strictEqual(fixResult.messages.length, 1); + }); + + it("should throw an error if fix is passed but meta has no `fixable` property", () => { + + const config = { + plugins: { + test: { + rules: { + "test-rule": { + meta: { + docs: {}, + schema: [] + }, + create: context => ({ + Program(node) { + context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" })); + } + }) + } + } + } + }, + rules: { + "test/test-rule": "error" + } + }; + + + assert.throws(() => { + linter.verify("0", config); + }, /Fixable rules must set the `meta\.fixable` property to "code" or "whitespace".\nOccurred while linting :1\nRule: "test\/test-rule"$/u); + }); + + it("should throw an error if fix is passed and there is no metadata", () => { + + const config = { + plugins: { + test: { + rules: { + "test-rule": { + create: context => ({ + Program(node) { + context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" })); + } + }) + } + } + } + }, + rules: { + "test/test-rule": "error" + } + }; + + assert.throws(() => { + linter.verify("0", config); + }, /Fixable rules must set the `meta\.fixable` property/u); + }); + + it("should throw an error if fix is passed from a legacy-format rule", () => { + + const config = { + plugins: { + test: { + rules: { + "test-rule": context => ({ + Program(node) { + context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" })); + } + }) + } + } + }, + rules: { + "test/test-rule": "error" + } + }; + + + assert.throws(() => { + linter.verify("0", config); + }, /Fixable rules must set the `meta\.fixable` property/u); + }); + }); + + describe("Mutability", () => { + let linter1 = null; + let linter2 = null; + + beforeEach(() => { + linter1 = new Linter(); + linter2 = new Linter(); + }); + + describe("rules", () => { + it("with no changes, same rules are loaded", () => { + assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys())); + }); + + it("loading rule in one doesn't change the other", () => { + linter1.defineRule("mock-rule", () => ({})); + + assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present"); + assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present"); + }); + }); + }); + + + describe("processors", () => { + let receivedFilenames = []; + let receivedPhysicalFilenames = []; + const extraConfig = { + plugins: { + test: { + rules: { + "report-original-text": { + meta: { + + }, + create(context) { + return { + Program(ast) { + receivedFilenames.push(context.getFilename()); + receivedPhysicalFilenames.push(context.getPhysicalFilename()); + context.report({ node: ast, message: context.getSourceCode().text }); + } + }; + } + } + } + } + } + }; + + beforeEach(() => { + receivedFilenames = []; + receivedPhysicalFilenames = []; + }); + + describe("preprocessors", () => { + it("should receive text and filename.", () => { + const code = "foo bar baz"; + const preprocess = sinon.spy(text => text.split(" ")); + const configs = createFlatConfigArray({}); + + configs.normalizeSync(); + + linter.verify(code, configs, { filename, preprocess }); + + assert.strictEqual(preprocess.calledOnce, true, "preprocess wasn't called"); + assert.deepStrictEqual(preprocess.args[0], [code, filename], "preprocess was called with the wrong arguments"); + }); + + it("should run preprocess only once", () => { + const logs = []; + const config = { + files: ["*.md"], + processor: { + preprocess(text, filenameForText) { + logs.push({ + text, + filename: filenameForText + }); + + return [{ text: "bar", filename: "0.js" }]; + }, + postprocess() { + return []; + } + } + }; + + linter.verify("foo", config, "a.md"); + assert.strictEqual(logs.length, 1, "preprocess() should only be called once."); + }); + + it("should apply a preprocessor to the code, and lint each code sample separately", () => { + const code = "foo bar baz"; + const configs = createFlatConfigArray([ + extraConfig, + { rules: { "test/report-original-text": "error" } } + ]); + + configs.normalizeSync(); + + const problems = linter.verify( + code, + configs, + { + filename, + + // Apply a preprocessor that splits the source text into spaces and lints each word individually + preprocess(input) { + return input.split(" "); + } + } + ); + + assert.strictEqual(problems.length, 3); + assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]); + }); + + it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => { + const code = "foo bar baz"; + const configs = createFlatConfigArray([ + extraConfig, + { rules: { "test/report-original-text": "error" } } + ]); + + configs.normalizeSync(); + + const problems = linter.verify( + code, + configs, + { + filename, + + // Apply a preprocessor that splits the source text into spaces and lints each word individually + preprocess(input) { + return input.split(" ").map(text => ({ + filename: "block.js", + text + })); + } + } + ); + + assert.strictEqual(problems.length, 3); + assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]); + + // filename + assert.strictEqual(receivedFilenames.length, 3); + assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0])); + assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1])); + assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2])); + + // physical filename + assert.strictEqual(receivedPhysicalFilenames.length, 3); + assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true); + }); + + it("should receive text even if a SourceCode object was given.", () => { + const code = "foo"; + const preprocess = sinon.spy(text => text.split(" ")); + const configs = createFlatConfigArray([ + extraConfig + ]); + + configs.normalizeSync(); + + linter.verify(code, configs); + const sourceCode = linter.getSourceCode(); + + linter.verify(sourceCode, configs, { filename, preprocess }); + + assert.strictEqual(preprocess.calledOnce, true); + assert.deepStrictEqual(preprocess.args[0], [code, filename]); + }); + + it("should receive text even if a SourceCode object was given (with BOM).", () => { + const code = "\uFEFFfoo"; + const preprocess = sinon.spy(text => text.split(" ")); + const configs = createFlatConfigArray([ + extraConfig + ]); + + configs.normalizeSync(); + + linter.verify(code, configs); + const sourceCode = linter.getSourceCode(); + + linter.verify(sourceCode, configs, { filename, preprocess }); + + assert.strictEqual(preprocess.calledOnce, true); + assert.deepStrictEqual(preprocess.args[0], [code, filename]); + }); + }); + + describe("postprocessors", () => { + it("should receive result and filename.", () => { + const code = "foo bar baz"; + const preprocess = sinon.spy(text => text.split(" ")); + const postprocess = sinon.spy(text => [text]); + const configs = createFlatConfigArray([ + extraConfig + ]); + + configs.normalizeSync(); + + linter.verify(code, configs, { filename, postprocess, preprocess }); + + assert.strictEqual(postprocess.calledOnce, true); + assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]); + }); + + it("should apply a postprocessor to the reported messages", () => { + const code = "foo bar baz"; + const configs = createFlatConfigArray([ + extraConfig, + { rules: { "test/report-original-text": "error" } } + ]); + + configs.normalizeSync(); + + const problems = linter.verify( + code, + configs, + { + preprocess: input => input.split(" "), + + /* + * Apply a postprocessor that updates the locations of the reported problems + * to make sure they correspond to the locations in the original text. + */ + postprocess(problemLists) { + problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1)); + return problemLists.reduce( + (combinedList, problemList, index) => + combinedList.concat( + problemList.map( + problem => + Object.assign( + {}, + problem, + { + message: problem.message.toUpperCase(), + column: problem.column + index * 4 + } + ) + ) + ), + [] + ); + } + } + ); + + assert.strictEqual(problems.length, 3); + assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]); + assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]); + }); + + it("should use postprocessed problem ranges when applying autofixes", () => { + const code = "foo bar baz"; + const configs = createFlatConfigArray([ + extraConfig, + { + plugins: { + test2: { + rules: { + "capitalize-identifiers": { + meta: { + fixable: "code" + }, + create(context) { + return { + Identifier(node) { + if (node.name !== node.name.toUpperCase()) { + context.report({ + node, + message: "Capitalize this identifier", + fix: fixer => fixer.replaceText(node, node.name.toUpperCase()) + }); + } + } + }; + } + } + } + } + } + }, + { rules: { "test2/capitalize-identifiers": "error" } } + ]); + + configs.normalizeSync(); + + const fixResult = linter.verifyAndFix( + code, + configs, + { + + /* + * Apply a postprocessor that updates the locations of autofixes + * to make sure they correspond to locations in the original text. + */ + preprocess: input => input.split(" "), + postprocess(problemLists) { + return problemLists.reduce( + (combinedProblems, problemList, blockIndex) => + combinedProblems.concat( + problemList.map(problem => + Object.assign(problem, { + fix: { + text: problem.fix.text, + range: problem.fix.range.map( + rangeIndex => rangeIndex + blockIndex * 4 + ) + } + })) + ), + [] + ); + } + } + ); + + assert.strictEqual(fixResult.fixed, true); + assert.strictEqual(fixResult.messages.length, 0); + assert.strictEqual(fixResult.output, "FOO BAR BAZ"); + }); + }); + }); + + describe("Edge cases", () => { + + describe("Modules", () => { + const moduleConfig = { + languageOptions: { + sourceType: "module", + ecmaVersion: 6 + } + }; + + it("should properly parse import statements when sourceType is module", () => { + const code = "import foo from 'foo';"; + const messages = linter.verify(code, moduleConfig); + + assert.strictEqual(messages.length, 0, "Unexpected linting error."); + }); + + it("should properly parse import all statements when sourceType is module", () => { + const code = "import * as foo from 'foo';"; + const messages = linter.verify(code, moduleConfig); + + assert.strictEqual(messages.length, 0, "Unexpected linting error."); + }); + + it("should properly parse default export statements when sourceType is module", () => { + const code = "export default function initialize() {}"; + const messages = linter.verify(code, moduleConfig); + + assert.strictEqual(messages.length, 0, "Unexpected linting error."); + }); + + }); + + + // https://github.com/eslint/eslint/issues/9687 + it("should report an error when invalid languageOptions found", () => { + let messages = linter.verify("", { languageOptions: { ecmaVersion: 222 } }); + + assert.deepStrictEqual(messages.length, 1); + assert.ok(messages[0].message.includes("Invalid ecmaVersion")); + + assert.throws(() => { + linter.verify("", { languageOptions: { sourceType: "foo" } }); + }, /Expected "script", "module", or "commonjs"./u); + + + messages = linter.verify("", { languageOptions: { ecmaVersion: 5, sourceType: "module" } }); + + assert.deepStrictEqual(messages.length, 1); + assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015")); + }); + + it("should not crash when invalid parentheses syntax is encountered", () => { + linter.verify("left = (aSize.width/2) - ()"); + }); + + it("should not crash when let is used inside of switch case", () => { + linter.verify("switch(foo) { case 1: let bar=2; }", { languageOptions: { ecmaVersion: 6 } }); + }); + + it("should not crash when parsing destructured assignment", () => { + linter.verify("var { a='a' } = {};", { languageOptions: { ecmaVersion: 6 } }); + }); + + it("should report syntax error when a keyword exists in object property shorthand", () => { + const messages = linter.verify("let a = {this}", { languageOptions: { ecmaVersion: 6 } }); + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].fatal, true); + }); + + it("should not crash when we reuse the SourceCode object", () => { + const config = { + languageOptions: { + ecmaVersion: 6, + parserOptions: { + ecmaFeatures: { jsx: true } + } + } + }; + + linter.verify("function render() { return
{hello}
}", config); + linter.verify(linter.getSourceCode(), config); + }); + + it("should reuse the SourceCode object", () => { + let ast1 = null, + ast2 = null; + + const config = { + plugins: { + test: { + rules: { + "save-ast1": () => ({ + Program(node) { + ast1 = node; + } + }), + + "save-ast2": () => ({ + Program(node) { + ast2 = node; + } + }) + + } + } + }, + languageOptions: { + ecmaVersion: 6, + parserOptions: { + ecmaFeatures: { jsx: true } + } + } + }; + + + linter.verify("function render() { return
{hello}
}", { ...config, rules: { "test/save-ast1": "error" } }); + linter.verify(linter.getSourceCode(), { ...config, rules: { "test/save-ast2": "error" } }); + + assert(ast1 !== null); + assert(ast2 !== null); + assert(ast1 === ast2); + }); + + it("should not modify config object passed as argument", () => { + const config = {}; + + Object.freeze(config); + linter.verify("var", config); + }); + + it("should pass 'id' to rule contexts with the rule id", () => { + + const spy = sinon.spy(context => { + assert.strictEqual(context.id, "test/foo-bar-baz"); + return {}; + }); + + const config = { + plugins: { + test: { + rules: { + "foo-bar-baz": spy + } + } + }, + rules: { + "test/foo-bar-baz": "error" + } + }; + + + linter.verify("x", config); + assert(spy.calledOnce); + }); + + + describe("when evaluating an empty string", () => { + it("runs rules", () => { + + const config = { + plugins: { + test: { + rules: { + "no-programs": context => ({ + Program(node) { + context.report({ node, message: "No programs allowed." }); + } + }) + } + } + }, + rules: { + "test/no-programs": "error" + } + }; + + assert.strictEqual( + linter.verify("", config).length, + 1 + ); + }); + }); + + }); + +}); diff --git a/eslint/tests/lib/rule-tester/rule-tester.js b/eslint/tests/lib/rule-tester/rule-tester.js index 6ebd82f..5018a03 100644 --- a/eslint/tests/lib/rule-tester/rule-tester.js +++ b/eslint/tests/lib/rule-tester/rule-tester.js @@ -1042,6 +1042,7 @@ describe("RuleTester", () => { }); assert.strictEqual(spy.args[1][1].parser, require.resolve("esprima")); }); + it("should pass normalized ecmaVersion to the rule", () => { const reportEcmaVersionRule = { meta: { @@ -1169,6 +1170,8 @@ describe("RuleTester", () => { parserOptions: { ecmaVersion: "latest" }, env: { es2020: true } }, + + // Non-Espree parsers normalize ecmaVersion if it's not "latest" { code: "", errors: [{ messageId: "ecmaVersionMessage", data: { type: "undefined", ecmaVersion: "undefined" } }], @@ -1194,7 +1197,7 @@ describe("RuleTester", () => { }, { code: "", - errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }], + errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: 6 } }], parser: notEspree, parserOptions: { ecmaVersion: 2015 } }, diff --git a/eslint/tests/lib/rules/max-lines-per-function.js b/eslint/tests/lib/rules/max-lines-per-function.js index ba01206..74acab2 100644 --- a/eslint/tests/lib/rules/max-lines-per-function.js +++ b/eslint/tests/lib/rules/max-lines-per-function.js @@ -199,7 +199,14 @@ if ( x === y ) { code: "function name() {\n}", options: [1], errors: [ - { messageId: "exceed", data: { name: "Function 'name'", lineCount: 2, maxLines: 1 } } + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 1, maxLines: 1 }, + line: 2, + column: 1, + endLine: 2, + endColumn: 2 + } ] }, @@ -208,7 +215,14 @@ if ( x === y ) { code: "var func = function() {\n}", options: [1], errors: [ - { messageId: "exceed", data: { name: "Function", lineCount: 2, maxLines: 1 } } + { + messageId: "exceed", + data: { name: "Function", linesExceed: 1, maxLines: 1 }, + line: 2, + column: 1, + endLine: 2, + endColumn: 2 + } ] }, @@ -217,7 +231,14 @@ if ( x === y ) { code: "const bar = () => {\nconst x = 2 + 1;\nreturn x;\n}", options: [3], errors: [ - { messageId: "exceed", data: { name: "Arrow function", lineCount: 4, maxLines: 3 } } + { + messageId: "exceed", + data: { name: "Arrow function", linesExceed: 1, maxLines: 3 }, + line: 4, + column: 1, + endLine: 4, + endColumn: 2 + } ] }, @@ -226,7 +247,14 @@ if ( x === y ) { code: "const bar = () =>\n 2", options: [1], errors: [ - { messageId: "exceed", data: { name: "Arrow function", lineCount: 2, maxLines: 1 } } + { + messageId: "exceed", + data: { name: "Arrow function", linesExceed: 1, maxLines: 1 }, + line: 2, + column: 1, + endLine: 2, + endColumn: 3 + } ] }, @@ -235,7 +263,14 @@ if ( x === y ) { code: `() => {${"foo\n".repeat(60)}}`, options: [{}], errors: [ - { messageId: "exceed", data: { name: "Arrow function", lineCount: 61, maxLines: 50 } } + { + messageId: "exceed", + data: { name: "Arrow function", linesExceed: 11, maxLines: 50 }, + line: 51, + column: 1, + endLine: 61, + endColumn: 2 + } ] }, @@ -244,7 +279,14 @@ if ( x === y ) { code: "function name() {\nvar x = 5;\n\t\n \n\nvar x = 2;\n}", options: [{ max: 6, skipComments: false, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Function 'name'", lineCount: 7, maxLines: 6 } } + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 1, maxLines: 6 }, + line: 7, + column: 1, + endLine: 7, + endColumn: 2 + } ] }, @@ -253,7 +295,14 @@ if ( x === y ) { code: "function name() {\r\nvar x = 5;\r\n\t\r\n \r\n\r\nvar x = 2;\r\n}", options: [{ max: 6, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Function 'name'", lineCount: 7, maxLines: 6 } } + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 1, maxLines: 6 }, + line: 7, + column: 1, + endLine: 7, + endColumn: 2 + } ] }, @@ -262,7 +311,14 @@ if ( x === y ) { code: "function name() {\nvar x = 5;\n\t\n \n\nvar x = 2;\n}", options: [{ max: 2, skipComments: true, skipBlankLines: true }], errors: [ - { messageId: "exceed", data: { name: "Function 'name'", lineCount: 4, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 2, maxLines: 2 }, + line: 6, + column: 1, + endLine: 7, + endColumn: 2 + } ] }, @@ -271,7 +327,14 @@ if ( x === y ) { code: "function name() {\r\nvar x = 5;\r\n\t\r\n \r\n\r\nvar x = 2;\r\n}", options: [{ max: 2, skipComments: true, skipBlankLines: true }], errors: [ - { messageId: "exceed", data: { name: "Function 'name'", lineCount: 4, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 2, maxLines: 2 }, + line: 6, + column: 1, + endLine: 7, + endColumn: 2 + } ] }, @@ -280,7 +343,14 @@ if ( x === y ) { code: "function name() { // end of line comment\nvar x = 5; /* mid line comment */\n\t// single line comment taking up whole line\n\t\n \n\nvar x = 2;\n}", options: [{ max: 6, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Function 'name'", lineCount: 7, maxLines: 6 } } + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 1, maxLines: 6 }, + line: 8, + column: 1, + endLine: 8, + endColumn: 2 + } ] }, @@ -289,7 +359,14 @@ if ( x === y ) { code: "function name() { // end of line comment\nvar x = 5; /* mid line comment */\n\t// single line comment taking up whole line\n\t\n \n\nvar x = 2;\n}", options: [{ max: 1, skipComments: true, skipBlankLines: true }], errors: [ - { messageId: "exceed", data: { name: "Function 'name'", lineCount: 4, maxLines: 1 } } + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 3, maxLines: 1 }, + line: 2, + column: 1, + endLine: 8, + endColumn: 2 + } ] }, @@ -298,7 +375,14 @@ if ( x === y ) { code: "function name() { // end of line comment\nvar x = 5; /* mid line comment */\n\t// single line comment taking up whole line\n\t\n \n\nvar x = 2;\n}", options: [{ max: 1, skipComments: false, skipBlankLines: true }], errors: [ - { messageId: "exceed", data: { name: "Function 'name'", lineCount: 5, maxLines: 1 } } + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 4, maxLines: 1 }, + line: 2, + column: 1, + endLine: 8, + endColumn: 2 + } ] }, @@ -313,7 +397,14 @@ if ( x === y ) { }`, options: [{ max: 2, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Function 'foo'", lineCount: 7, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Function 'foo'", linesExceed: 5, maxLines: 2 }, + line: 3, + column: 1, + endLine: 7, + endColumn: 2 + } ] }, @@ -328,7 +419,14 @@ function ()`, options: [{ max: 2, skipComments: true, skipBlankLines: false, IIFEs: true }], errors: [ - { messageId: "exceed", data: { name: "Function", lineCount: 4, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Function", linesExceed: 2, maxLines: 2 }, + line: 4, + column: 1, + endLine: 5, + endColumn: 2 + } ] }, @@ -346,7 +444,14 @@ if ( x === y ) { }`, options: [{ max: 9, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Function 'parent'", lineCount: 10, maxLines: 9 } } + { + messageId: "exceed", + data: { name: "Function 'parent'", linesExceed: 1, maxLines: 9 }, + line: 10, + column: 1, + endLine: 10, + endColumn: 2 + } ] }, @@ -364,8 +469,22 @@ if ( x === y ) { }`, options: [{ max: 2, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Function 'parent'", lineCount: 10, maxLines: 2 } }, - { messageId: "exceed", data: { name: "Function 'nested'", lineCount: 4, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Function 'parent'", linesExceed: 8, maxLines: 2 }, + line: 3, + column: 1, + endLine: 10, + endColumn: 2 + }, + { + messageId: "exceed", + data: { name: "Function 'nested'", linesExceed: 2, maxLines: 2 }, + line: 5, + column: 1, + endLine: 6, + endColumn: 2 + } ] }, @@ -380,7 +499,14 @@ if ( x === y ) { }`, options: [{ max: 2, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Method 'method'", lineCount: 5, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Method 'method'", linesExceed: 3, maxLines: 2 }, + line: 4, + column: 1, + endLine: 6, + endColumn: 6 + } ] }, @@ -395,7 +521,14 @@ if ( x === y ) { }`, options: [{ max: 2, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Static method 'foo'", lineCount: 5, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Static method 'foo'", linesExceed: 3, maxLines: 2 }, + line: 4, + column: 1, + endLine: 6, + endColumn: 6 + } ] }, @@ -408,9 +541,16 @@ if ( x === y ) { return 1 } }`, - options: [{ max: 2, skipComments: true, skipBlankLines: false }], + options: [{ max: 4, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Getter 'foo'", lineCount: 5, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Getter 'foo'", linesExceed: 1, maxLines: 4 }, + line: 6, + column: 1, + endLine: 6, + endColumn: 6 + } ] }, @@ -425,7 +565,14 @@ if ( x === y ) { }`, options: [{ max: 2, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Setter 'foo'", lineCount: 5, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Setter 'foo'", linesExceed: 3, maxLines: 2 }, + line: 4, + column: 1, + endLine: 6, + endColumn: 6 + } ] }, @@ -443,7 +590,14 @@ if ( x === y ) { }`, options: [{ max: 2, skipComments: true, skipBlankLines: false }], errors: [ - { messageId: "exceed", data: { name: "Static method", lineCount: 8, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Static method", linesExceed: 6, maxLines: 2 }, + line: 4, + column: 1, + endLine: 9, + endColumn: 6 + } ] }, @@ -458,7 +612,14 @@ if ( x === y ) { }());`, options: [{ max: 2, skipComments: true, skipBlankLines: false, IIFEs: true }], errors: [ - { messageId: "exceed", data: { name: "Function", lineCount: 7, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Function", linesExceed: 5, maxLines: 2 }, + line: 3, + column: 1, + endLine: 7, + endColumn: 2 + } ] }, @@ -473,7 +634,28 @@ if ( x === y ) { })();`, options: [{ max: 2, skipComments: true, skipBlankLines: false, IIFEs: true }], errors: [ - { messageId: "exceed", data: { name: "Arrow function", lineCount: 7, maxLines: 2 } } + { + messageId: "exceed", + data: { name: "Arrow function", linesExceed: 5, maxLines: 2 }, + line: 3, + column: 1, + endLine: 7, + endColumn: 2 + } + ] + }, + { + code: "\nfoo();\nbar();\nbaz();\nfunction name() {\nvar x = 5;\n/* comment 1 */\n/* comment 2 */\n\t\n \n\nvar x = 2;\n}\nquz();", + options: [{ max: 2, skipComments: true, skipBlankLines: true }], + errors: [ + { + messageId: "exceed", + data: { name: "Function 'name'", linesExceed: 2, maxLines: 2 }, + line: 12, + column: 1, + endLine: 13, + endColumn: 2 + } ] } ] diff --git a/eslint/tests/lib/rules/no-constant-condition.js b/eslint/tests/lib/rules/no-constant-condition.js index ecb6de5..0b5f3c5 100644 --- a/eslint/tests/lib/rules/no-constant-condition.js +++ b/eslint/tests/lib/rules/no-constant-condition.js @@ -174,7 +174,8 @@ ruleTester.run("no-constant-condition", rule, { "function* foo() {for (; ; yield) {}}", "function* foo() {while (true) {function* foo() {yield;}yield;}}", "function* foo() { for (let x = yield; x < 10; x++) {yield;}yield;}", - "function* foo() { for (let x = yield; ; x++) { yield; }}" + "function* foo() { for (let x = yield; ; x++) { yield; }}", + "if (new Number(x) + 1 === 2) {}" ], invalid: [ { code: "for(;true;);", errors: [{ messageId: "unexpected", type: "Literal" }] }, @@ -387,6 +388,16 @@ ruleTester.run("no-constant-condition", rule, { { code: "if(0b1n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] }, { code: "if(0o1n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] }, { code: "if(0x1n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] }, - { code: "if(0x1n || foo);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "LogicalExpression" }] } + { code: "if(0x1n || foo);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "LogicalExpression" }] }, + + // Classes and instances are always truthy + { code: "if(class {}) {}", errors: [{ messageId: "unexpected" }] }, + { code: "if(new Foo()) {}", errors: [{ messageId: "unexpected" }] }, + + // Boxed primitives are always truthy + { code: "if(new Boolean(foo)) {}", errors: [{ messageId: "unexpected" }] }, + { code: "if(new String(foo)) {}", errors: [{ messageId: "unexpected" }] }, + { code: "if(new Number(foo)) {}", errors: [{ messageId: "unexpected" }] } + ] }); diff --git a/eslint/tools/update-readme.js b/eslint/tools/update-readme.js index 55efb6f..0704cef 100644 --- a/eslint/tools/update-readme.js +++ b/eslint/tools/update-readme.js @@ -123,5 +123,8 @@ let newReadme = readme.replace(/[\w\W]*?/u, ejs.re newReadme = newReadme.replace(/[\w\W]*?/u, formatSponsors(allSponsors)); +// replace multiple consecutive blank lines with just one blank line +newReadme = newReadme.replace(/(?<=^|\n)\n{2,}/gu, "\n"); + // output to the file fs.writeFileSync(README_FILE_PATH, newReadme, "utf8");