├── .all-contributorsrc ├── .babelrc ├── .eslintrc ├── .github └── workflows │ ├── node-pretest.yml │ ├── node.yml │ ├── rebase.yml │ └── require-allow-edits.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── eslint.config.js ├── package.json ├── src ├── bin │ ├── diff.js │ └── find.js └── lib │ ├── array-diff.js │ ├── cli-util.js │ ├── flatten-rules-diff.js │ ├── normalize-plugin-name.js │ ├── object-diff.js │ ├── rule-finder.js │ ├── sort-rules.js │ └── stringify-rule-config.js └── test ├── bin ├── diff.js └── find.js ├── fixtures ├── post-v8 │ ├── eslint-dedupe-plugin-rules.js │ ├── eslint-flat-config.js │ ├── eslint-with-deprecated-rules.js │ ├── eslint-with-overrides.js │ ├── eslint-with-plugin-with-no-rules.js │ ├── eslint_json.js │ ├── eslint_yml.js │ ├── eslintrc.js │ └── no-path │ │ ├── index.js │ │ ├── index.txt │ │ └── package.json └── prior-v8 │ ├── eslint-dedupe-plugin-rules.json │ ├── eslint-flat-config.js │ ├── eslint-with-deprecated-rules.json │ ├── eslint-with-overrides.json │ ├── eslint-with-plugin-with-no-rules.json │ ├── eslint.json │ ├── eslint.yml │ ├── eslintrc │ └── no-path │ ├── index.js │ ├── index.txt │ └── package.json ├── lib ├── array-diff.js ├── cli-util.js ├── flatten-rules-diff.js ├── object-diff.js ├── rule-finder.js ├── sort-rules.js └── stringify-rule-config.js └── mocha.opts /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectOwner": "sarbbottam", 3 | "projectName": "eslint-find-rules", 4 | "imageSize": 100, 5 | "contributors": [ 6 | { 7 | "login": "sarbbottam", 8 | "name": "Sarbbottam Bandyopadhyay", 9 | "avatar_url": "https://avatars1.githubusercontent.com/u/949380?v=3", 10 | "profile": "https://twitter.com/sarbbottam", 11 | "contributions": [ 12 | "code", 13 | "doc", 14 | "test", 15 | "review" 16 | ] 17 | }, 18 | { 19 | "login": "ta2edchimp", 20 | "name": "Andreas Windt", 21 | "avatar_url": "https://avatars1.githubusercontent.com/u/262436?v=3", 22 | "profile": "https://twitter.com/ta2edchimp", 23 | "contributions": [ 24 | "code", 25 | "doc", 26 | "test", 27 | "review" 28 | ] 29 | }, 30 | { 31 | "login": "kentcdodds", 32 | "name": "Kent C. Dodds", 33 | "avatar_url": "https://avatars3.githubusercontent.com/u/1500684?v=3", 34 | "profile": "https://twitter.com/kentcdodds", 35 | "contributions": [ 36 | "code", 37 | "doc", 38 | "test", 39 | "review" 40 | ] 41 | }, 42 | { 43 | "login": "scottnonnenberg", 44 | "name": "Scott Nonnenberg", 45 | "avatar_url": "https://avatars1.githubusercontent.com/u/443005?v=3", 46 | "profile": "https://github.com/scottnonnenberg", 47 | "contributions": [ 48 | "code", 49 | "test" 50 | ] 51 | }, 52 | { 53 | "login": "mgol", 54 | "name": "Michał Gołębiowski", 55 | "avatar_url": "https://avatars3.githubusercontent.com/u/1758366?v=3", 56 | "profile": "https://github.com/mgol", 57 | "contributions": [ 58 | "code" 59 | ] 60 | }, 61 | { 62 | "login": "jfmengels", 63 | "name": "Jeroen Engels", 64 | "avatar_url": "https://avatars.githubusercontent.com/u/3869412?v=3", 65 | "profile": "https://github.com/jfmengels", 66 | "contributions": [ 67 | "doc" 68 | ] 69 | }, 70 | { 71 | "login": "dustinspecker", 72 | "name": "Dustin Specker", 73 | "avatar_url": "https://avatars2.githubusercontent.com/u/2449282?v=3", 74 | "profile": "https://github.com/dustinspecker", 75 | "contributions": [ 76 | "code" 77 | ] 78 | }, 79 | { 80 | "login": "randycoulman", 81 | "name": "Randy Coulman", 82 | "avatar_url": "https://avatars1.githubusercontent.com/u/1406203?v=3", 83 | "profile": "https://github.com/randycoulman", 84 | "contributions": [ 85 | "code", 86 | "test" 87 | ] 88 | }, 89 | { 90 | "login": "ljharb", 91 | "name": "Jordan Harband", 92 | "avatar_url": "https://avatars1.githubusercontent.com/u/45469?v=4", 93 | "profile": "https://twitter.com/ljharb", 94 | "contributions": [ 95 | "doc", 96 | "bug", 97 | "code", 98 | "question", 99 | "review", 100 | "test", 101 | "infra" 102 | ] 103 | }, 104 | { 105 | "login": "bradzacher", 106 | "name": "Brad Zacher", 107 | "avatar_url": "https://avatars1.githubusercontent.com/u/7462525?v=4", 108 | "profile": "https://zacher.com.au", 109 | "contributions": [ 110 | "code", 111 | "test" 112 | ] 113 | } 114 | ], 115 | "repoType": "github" 116 | } 117 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "node": "18" 6 | }, 7 | "exclude": ["transform-regenerator"] 8 | }] 9 | ], 10 | "plugins": [ 11 | "transform-object-rest-spread", 12 | "transform-class-properties" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2022 9 | }, 10 | "plugins": ["json"], 11 | "root": true, 12 | "ignorePatterns": [ 13 | "node_modules/", 14 | "coverage/", 15 | ".nyc_output/", 16 | ], 17 | "reportUnusedDisableDirectives": true, 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/node-pretest.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: pretest/posttest' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | pretest: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: ljharb/actions/node/install@main 12 | name: 'nvm install lts/* && npm install' 13 | with: 14 | node-version: 'lts/*' 15 | - run: npm run pretest 16 | 17 | # posttest: 18 | # runs-on: ubuntu-latest 19 | 20 | # steps: 21 | # - uses: actions/checkout@v2 22 | # - uses: ljharb/actions/node/install@main 23 | # name: 'nvm install lts/* && npm install' 24 | # with: 25 | # node-version: 'lts/*' 26 | # - run: npm run posttest 27 | -------------------------------------------------------------------------------- /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | matrix: 7 | runs-on: ubuntu-latest 8 | outputs: 9 | majors: ${{ steps.set-matrix.outputs.requireds }} 10 | steps: 11 | - uses: ljharb/actions/node/matrix@main 12 | id: set-matrix 13 | with: 14 | versionsAsRoot: true 15 | type: 'majors' 16 | preset: '>=18' 17 | 18 | majors: 19 | needs: [matrix] 20 | name: 'latest majors' 21 | runs-on: ubuntu-latest 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | node-version: ${{ fromJson(needs.matrix.outputs.majors) }} 27 | eslint: 28 | - 8 29 | - 9 30 | exclude: 31 | - node-version: 19 32 | eslint: 9 33 | 34 | steps: 35 | - uses: actions/checkout@v2 36 | - uses: ljharb/actions/node/install@main 37 | name: 'nvm install ${{ matrix.node-version }} && npm install' 38 | with: 39 | node-version: ${{ matrix.node-version }} 40 | skip-ls-check: true 41 | - run: npm install --no-save eslint@${{ matrix.eslint }} 42 | - run: npm prune > /dev/null 43 | - run: npm ls > /dev/null 44 | - run: npm run cover 45 | - uses: codecov/codecov-action@v2 46 | 47 | node: 48 | name: 'node 18+' 49 | needs: [majors] 50 | runs-on: ubuntu-latest 51 | steps: 52 | - run: 'echo tests completed' 53 | -------------------------------------------------------------------------------- /.github/workflows/rebase.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Rebase 2 | 3 | on: [pull_request_target] 4 | 5 | jobs: 6 | _: 7 | name: "Automatic Rebase" 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: ljharb/rebase@master 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/require-allow-edits.yml: -------------------------------------------------------------------------------- 1 | name: Require “Allow Edits” 2 | 3 | on: [pull_request_target] 4 | 5 | jobs: 6 | _: 7 | name: "Require “Allow Edits”" 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: ljharb/require-allow-edits@main 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .nyc_output/ 3 | coverage/ 4 | *.log 5 | dist/ 6 | .opt-in 7 | 8 | # Only apps should have lockfiles 9 | npm-shrinkwrap.json 10 | package-lock.json 11 | yarn.lock 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .nyc_output/ 3 | coverage/ 4 | *.log 5 | .opt-in 6 | 7 | .github/workflows 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release). 4 | You can see it on the [releases page](https://github.com/sarbbottam/eslint-find-rules/releases). 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | **Working on your first Pull Request?** You can learn how from this *free* series 4 | [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) 5 | 6 | ## Steps 7 | 8 | 1. File an issue and discuss your proposed solution first 9 | 2. Fork and clone the repo 10 | 3. `$ npm install` to install dependencies 11 | 4. `$ npm run validate` to validate you've got it working 12 | 5. Create a branch for your PR 13 | 6. `$ npm run test -- --watch` to watch the file system as you make changes to things 14 | 7. Make your changes. Make sure to add yourself to the `.all-contributorsrc`! [More info](https://github.com/kentcdodds/all-contributors) 15 | 8. Run `npm run validate` to make sure things look good. 16 | 9. Commit your changes following [our standards](https://github.com/stevemao/conventional-changelog-angular/blob/master/convention.md) (optionally use `$ npm run commit` to craft a commit message and commit. If you're having trouble committing, try adding `--no-verify` to your `git commit` command and ask for help in the pull request :-) 17 | 10. Push your branch to your fork 18 | 11. Make a pull request to `master` on this repository 19 | 12. Get merged 20 | 13. Celebrate 🎉 21 | 22 | ## Committing and Pushing changes 23 | 24 | As stated earlier, please follow [this convention](https://github.com/stevemao/conventional-changelog-angular/blob/master/convention.md) for your commit messages. 25 | 26 | Once you are ready to commit the changes, please use the below commands 27 | 28 | 1. `git add ` 29 | 2. `$ npm run commit` 30 | 31 | ... and follow the instruction of the interactive prompt. 32 | 33 | ### Opt into run tests while committing 34 | 35 | `npm run commit` will not execute any `tests`, prior `commit`ing, however you can **opt into it**. 36 | 37 | In order to execute `tests` automatically before `commit`, create a file `.opt-in` in the root of the project with `pre-commit` as the content. 38 | 39 | Excute the following command in the root of the project to create the above stated file. 40 | 41 | `$ echo pre-commit > .opt-in` 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Kent C. Dodds 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-find-rules 2 | 3 | [![Join the chat at https://gitter.im/sarbbottam/eslint-find-rules](https://badges.gitter.im/sarbbottam/eslint-find-rules.svg)](https://gitter.im/sarbbottam/eslint-find-rules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | Use this for your own [ESLint](http://eslint.org/) [shareable configuration](http://eslint.org/docs/developer-guide/shareable-configs) 6 | to list current configured rules, all-available rules, unused rules, and plugin rules. 7 | 8 | [![Build Status](https://img.shields.io/travis/sarbbottam/eslint-find-rules.svg?style=flat-square)](https://travis-ci.org/sarbbottam/eslint-find-rules) 9 | [![Code Coverage](https://img.shields.io/codecov/c/github/sarbbottam/eslint-find-rules.svg?style=flat-square)](https://codecov.io/github/sarbbottam/eslint-find-rules) 10 | [![version](https://img.shields.io/npm/v/eslint-find-rules.svg?style=flat-square)](http://npm.im/eslint-find-rules) 11 | [![downloads](https://img.shields.io/npm/dm/eslint-find-rules.svg?style=flat-square)](http://npm-stat.com/charts.html?package=eslint-find-rules&from=2015-08-01) 12 | [![MIT License](https://img.shields.io/npm/l/eslint-find-rules.svg?style=flat-square)](http://opensource.org/licenses/MIT) 13 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 14 | [![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors) 15 | 16 | ## Acknowledgment 17 | 18 | This module is an extended version of [eslint-find-new-rules](https://github.com/kentcdodds/eslint-find-new-rules) 19 | 20 | ## Installation 21 | 22 | Simply install locally as a development dependency to your project's package: 23 | 24 | ``` 25 | npm install --save-dev eslint-find-rules 26 | ``` 27 | 28 | ## Usage 29 | 30 | > It is expected to be used as `local` utility, as it needs `eslint` and the `eslint-plugins` being referred by the `eslint-config` file, to be installed. 31 | Using it as a `global` utility, will error out, if `eslint` and the `eslint-plugins` being referred by the `eslint-config` file, are not installed globally. 32 | 33 | The intended usage is as an npm script: 34 | 35 | ```javascript 36 | { 37 | ... 38 | "scripts": { 39 | "eslint-find-option-rules": "eslint-find-rules [option] [flag]" 40 | } 41 | ... 42 | } 43 | ``` 44 | 45 | Then run it with: `$ npm run --silent eslint-find-option-rules` (the `--silent` is to silence npm output). 46 | 47 | ``` 48 | available options are -a|--all-available, -c|--current, -d|--deprecated, -p|--plugin, -u|--unused 49 | available flags are -n|--no-error, --no-core, -i/--include deprecated, --ext .js, and --flatConfig 50 | ``` 51 | 52 | By default it will error out only for `-d|--deprecated` and `-u|--unused`, 53 | however if you do not want the `process` to `exit` with a `non-zero` exit code, use the `-n|--no-error` flag along with `-d|--deprecated` or `-u|--unused`. 54 | 55 | By default, core rules will be included in the output of `-a|--all-available`, `-c|--current`, `-d|--deprecated`, and `-u|--unused`. If you want to report on plugin rules only, use the `--no-core` flag. 56 | 57 | By default, deprecated rules will be omitted from the output of `-a|--all-available`, `-p|--plugin` and `-u|--unused`. If you want to report on deprecated rules as well, use the `--include=deprecated` or `-i deprecated` flag. 58 | 59 | By default, rules will be searched for files having `.js` extension. If you want to find rules using another extension (`.json` for example), use the `--ext .json` flag (or `--ext .js --ext .json` if you need multiple extensions). 60 | 61 | By default, ESLint will handle configs in Legacy mode. If you want to handle Flat config files, you need to add the `--flatConfig` flag. 62 | 63 | **NOTE:** Deprecated rules are found by looking at the metadata of the rule definition. All core rules and many plugin rules use this flag to indicate deprecated rules. But if you find a plugin that does not mark their rules as deprecated in the rule metadata, please file a pull request with that project. 64 | 65 | ### Specify a file 66 | 67 | This is really handy in an actual config module (like [eslint-config-kentcdodds](https://github.com/kentcdodds/eslint-config-kentcdodds)) where you could also do: 68 | 69 | ``` 70 | // available options are -a|--all-available, -c|--current, -d|--deprecated, -p|--plugin, -u|--unused 71 | eslint-find-rules --option ./index.js 72 | ``` 73 | 74 | This is resolved, relative to the `process.cwd()` which, in the context of `npm` scripts is always the location of your `package.json`. 75 | 76 | You may specify any [config format supported by ESLint](http://eslint.org/docs/user-guide/configuring). 77 | 78 | ### Absolute Path 79 | 80 | You can also provide an absolute path: 81 | 82 | ``` 83 | eslint-find-rules --option ~/Developer/eslint-config-kentcdodds/index.js 84 | ``` 85 | 86 | **Please note** that any tested ESLint config file must reside below your project's root. 87 | 88 | ### Default to `main` 89 | 90 | It will also default to the `main` in your `package.json`, so you can omit the `path/to/file` argument: 91 | 92 | ``` 93 | eslint-find-rules --option 94 | ``` 95 | 96 | ### As a `require`d module 97 | 98 | ``` 99 | var getRuleFinder = require('./eslint-find-rules') 100 | var ruleFinder = getRuleFinder('path/to/eslint-config') 101 | 102 | // default to the `main` in your `package.json` 103 | // var ruleFinder = await getRuleFinder() 104 | 105 | // get all the current, plugin, available and unused rules 106 | // without referring the extended files or documentation 107 | 108 | ruleFinder.getCurrentRules() 109 | 110 | ruleFinder.getCurrentRulesDetailed() 111 | 112 | ruleFinder.getPluginRules() 113 | 114 | ruleFinder.getAllAvailableRules() 115 | 116 | ruleFinder.getUnusedRules() 117 | 118 | ruleFinder.getDeprecatedRules() 119 | ``` 120 | 121 | ### Log the difference between two config files 122 | 123 | ```javascript 124 | { 125 | ... 126 | "scripts": { 127 | "eslint-diff-rules": "eslint-diff-rules " 128 | } 129 | ... 130 | } 131 | ``` 132 | 133 | ## Contributors 134 | 135 | Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)): 136 | 137 | 138 | 139 | | [
Sarbbottam Bandyopadhyay](https://twitter.com/sarbbottam)
[💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=sarbbottam "Code") [📖](https://github.com/sarbbottam/eslint-find-rules/commits?author=sarbbottam "Documentation") [⚠️](https://github.com/sarbbottam/eslint-find-rules/commits?author=sarbbottam "Tests") [👀](#review-sarbbottam "Reviewed Pull Requests") | [
Andreas Windt](https://twitter.com/ta2edchimp)
[💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=ta2edchimp "Code") [📖](https://github.com/sarbbottam/eslint-find-rules/commits?author=ta2edchimp "Documentation") [⚠️](https://github.com/sarbbottam/eslint-find-rules/commits?author=ta2edchimp "Tests") [👀](#review-ta2edchimp "Reviewed Pull Requests") | [
Kent C. Dodds](https://twitter.com/kentcdodds)
[💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=kentcdodds "Code") [📖](https://github.com/sarbbottam/eslint-find-rules/commits?author=kentcdodds "Documentation") [⚠️](https://github.com/sarbbottam/eslint-find-rules/commits?author=kentcdodds "Tests") [👀](#review-kentcdodds "Reviewed Pull Requests") | [
Scott Nonnenberg](https://github.com/scottnonnenberg)
[💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=scottnonnenberg "Code") [⚠️](https://github.com/sarbbottam/eslint-find-rules/commits?author=scottnonnenberg "Tests") | [
Michał Gołębiowski](https://github.com/mgol)
[💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=mgol "Code") | [
Jeroen Engels](https://github.com/jfmengels)
[📖](https://github.com/sarbbottam/eslint-find-rules/commits?author=jfmengels "Documentation") | [
Dustin Specker](https://github.com/dustinspecker)
[💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=dustinspecker "Code") | 140 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 141 | | [
Randy Coulman](https://github.com/randycoulman)
[💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=randycoulman "Code") [⚠️](https://github.com/sarbbottam/eslint-find-rules/commits?author=randycoulman "Tests") | [
Jordan Harband](https://twitter.com/ljharb)
[📖](https://github.com/sarbbottam/eslint-find-rules/commits?author=ljharb "Documentation") [🐛](https://github.com/sarbbottam/eslint-find-rules/issues?q=author%3Aljharb "Bug reports") [💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=ljharb "Code") [💬](#question-ljharb "Answering Questions") [👀](#review-ljharb "Reviewed Pull Requests") [⚠️](https://github.com/sarbbottam/eslint-find-rules/commits?author=ljharb "Tests") [🚇](#infra-ljharb "Infrastructure (Hosting, Build-Tools, etc)") | [
Brad Zacher](https://zacher.com.au)
[💻](https://github.com/sarbbottam/eslint-find-rules/commits?author=bradzacher "Code") [⚠️](https://github.com/sarbbottam/eslint-find-rules/commits?author=bradzacher "Tests") | 142 | 143 | 144 | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. 145 | Contributions of any kind welcome! 146 | 147 | Special thanks to [@mgol](https://github.com/mgol) who created the original script. 148 | 149 | ## LICENSE 150 | 151 | MIT 152 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | const globals = require('globals'); 2 | const js = require('@eslint/js') 3 | const json = require('eslint-plugin-json'); 4 | 5 | module.exports = [ 6 | { 7 | files: [ 8 | "**/*.js", 9 | "**/*.mjs", 10 | "**/*.cjs", 11 | ], 12 | ...js.configs.recommended, 13 | 14 | languageOptions: { 15 | globals: { 16 | ...globals.commonjs, 17 | ...globals.es2015, 18 | ...globals.node, 19 | }, 20 | parserOptions: { 21 | ecmaVersion: 2022, 22 | }, 23 | }, 24 | }, 25 | { 26 | files: ['test/**/*.js'], 27 | languageOptions: { 28 | globals: { 29 | ...globals.mocha, 30 | }, 31 | }, 32 | }, 33 | { 34 | ignores: [ 35 | 'node_modules/**', 36 | 'coverage/**', 37 | '.nyc_output/**', 38 | 'dist/**', 39 | ], 40 | }, 41 | { 42 | files: ['**/*.json'], 43 | ...json.configs.recommended, 44 | }, 45 | ]; 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-find-rules", 3 | "version": "5.0.0", 4 | "description": "Find built-in ESLint rules you don't have in your custom config.", 5 | "main": "dist/lib/rule-finder.js", 6 | "exports": { 7 | ".": "./dist/lib/rule-finder.js", 8 | "./package.json": "./package.json" 9 | }, 10 | "scripts": { 11 | "cover": "nyc --reporter=lcov --reporter=text --reporter=html npm run tests-only", 12 | "lint": "eslint .", 13 | "pretest": "npm run lint", 14 | "tests-only": "mocha 'test/bin/**/*.js' 'test/lib/**/*.js' --recursive", 15 | "test": "npm run tests-only", 16 | "posttest": "npx npm@'>=10.2' audit --production", 17 | "update-contributors": "all-contributors generate", 18 | "commit": "git-cz", 19 | "validate": "npm-run-all --parallel lint cover --sequential check-coverage", 20 | "check-coverage": "nyc check-coverage --statements 100 --branches 100 --functions 100 --lines 100", 21 | "prebuild": "rimraf dist", 22 | "build": "babel src -d dist", 23 | "prepublish": "not-in-publish || npm run build" 24 | }, 25 | "bin": { 26 | "eslint-find-rules": "dist/bin/find.js", 27 | "eslint-diff-rules": "dist/bin/diff.js" 28 | }, 29 | "keywords": [], 30 | "author": "Michał Gołębiowski ", 31 | "contributors": [ 32 | "Kent C. Dodds (http://kentcdodds.com/)", 33 | "Sarbbottam Bandyopadhyay ", 34 | "Andreas Windt " 35 | ], 36 | "license": "MIT", 37 | "dependencies": { 38 | "cliui": "^7.0.4", 39 | "eslint-rule-documentation": "^1.0.23", 40 | "glob": "^7.2.3", 41 | "window-size": "^0.3.0", 42 | "yargs": "^16.2.0" 43 | }, 44 | "devDependencies": { 45 | "@eslint/js": "^9.18.0", 46 | "all-contributors-cli": "^4.11.2", 47 | "babel-cli": "^6.26.0", 48 | "babel-core": "^6.26.3", 49 | "babel-plugin-transform-class-properties": "^6.24.1", 50 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 51 | "babel-preset-env": "^1.7.0", 52 | "codecov": "^2.3.1", 53 | "commitizen": "^2.10.1", 54 | "create-require": "^1.1.1", 55 | "cz-conventional-changelog": "^2.1.0", 56 | "eslint": "^8.57.1 || ^9.18.0", 57 | "eslint-plugin-json": "^4.0.1", 58 | "ghooks": "^2.0.4", 59 | "globals": "^15.14.0", 60 | "in-publish": "^2.0.1", 61 | "lodash.merge": "^4.6.2", 62 | "mocha": "^3.5.3", 63 | "npm-run-all": "^4.1.5", 64 | "nyc": "^11.9.0", 65 | "opt-cli": "^1.6.0", 66 | "proxyquire": "^1.8.0", 67 | "rimraf": "^2.7.1", 68 | "semver": "^6.3.1", 69 | "sinon": "^2.4.1", 70 | "validate-commit-msg": "^2.14.0" 71 | }, 72 | "peerDependencies": { 73 | "eslint": "^8.57.1 || ^9.18.0" 74 | }, 75 | "nyc": { 76 | "exclude": [ 77 | "test/**/*" 78 | ] 79 | }, 80 | "config": { 81 | "ghooks": { 82 | "commit-msg": "validate-commit-msg", 83 | "pre-commit": "opt --in pre-commit --exec \"npm run validate\"" 84 | }, 85 | "validate-commit-msg": { 86 | "types": [ 87 | "feat", 88 | "fix", 89 | "improvement", 90 | "docs", 91 | "style", 92 | "refactor", 93 | "perf", 94 | "test", 95 | "build", 96 | "ci", 97 | "breaking", 98 | "revert", 99 | "deps", 100 | "meta" 101 | ] 102 | }, 103 | "commitizen": { 104 | "path": "./node_modules/cz-conventional-changelog" 105 | } 106 | }, 107 | "repository": { 108 | "type": "git", 109 | "url": "https://github.com/sarbbottam/eslint-find-rules.git" 110 | }, 111 | "bugs": { 112 | "url": "https://github.com/sarbbottam/eslint-find-rules/issues" 113 | }, 114 | "homepage": "https://github.com/sarbbottam/eslint-find-rules#readme", 115 | "engines": { 116 | "node": "^18.20 || ^20.18 || ^22.13 || >= 23.6" 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/bin/diff.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | const argv = require('yargs') 7 | .boolean('verbose') 8 | .alias('v', 'verbose') 9 | .argv; 10 | 11 | const cli = require('../lib/cli-util'); 12 | 13 | const getRuleFinder = require('../lib/rule-finder'); 14 | const arrayDifference = require('../lib/array-diff'); 15 | const objectDifference = require('../lib/object-diff'); 16 | const getSortedRules = require('../lib/sort-rules'); 17 | const flattenRulesDiff = require('../lib/flatten-rules-diff'); 18 | const stringifyRuleConfig = require('../lib/stringify-rule-config'); 19 | 20 | (async function () { 21 | 22 | const files = [argv._[0], argv._[1]]; 23 | const collectedRules = await Promise.all(getFilesToCompare(files).map(compareConfigs)); 24 | 25 | const rulesCount = collectedRules.reduce( 26 | (prev, curr) => { 27 | return prev + (curr && curr.rules ? curr.rules.length : /* istanbul ignore next */ 0); 28 | }, 0); 29 | 30 | /* istanbul ignore else */ 31 | if (argv.verbose || rulesCount) { 32 | cli.push('\ndiff rules\n' + rulesCount + ' rules differ\n'); 33 | } 34 | 35 | /* istanbul ignore else */ 36 | if (rulesCount) { 37 | collectedRules.forEach(diff => { 38 | let rules = diff.rules; 39 | 40 | /* istanbul ignore if */ 41 | if (rules.length < 1) { 42 | return; 43 | } 44 | 45 | if (argv.verbose) { 46 | rules = flattenRulesDiff(rules).map(stringifyRuleConfig); 47 | rules.unshift([], diff.config1, diff.config2); 48 | } else { 49 | cli.push('\nin ' + diff.config1 + ' but not in ' + diff.config2 + ':\n'); 50 | } 51 | 52 | cli.push(rules, argv.verbose ? 3 : 0); 53 | }); 54 | } 55 | 56 | cli.write(); 57 | 58 | function getFilesToCompare(allFiles) { 59 | const filesToCompare = [allFiles]; 60 | 61 | if (!argv.verbose) { 62 | // In non-verbose output mode, compare a to b 63 | // and b to a afterwards, to obtain ALL differences 64 | // accross those files, but grouped 65 | filesToCompare.push([].concat(allFiles).reverse()); 66 | } 67 | 68 | return filesToCompare; 69 | } 70 | 71 | async function compareConfigs(currentFiles) { 72 | const ruleFinders = await Promise.all(currentFiles.slice(0, 2).map(getRuleFinder)); 73 | return { 74 | config1: path.basename(currentFiles[0]), 75 | config2: path.basename(currentFiles[1]), 76 | rules: rulesDifference( 77 | ruleFinders[0], 78 | ruleFinders[1] 79 | ) 80 | }; 81 | } 82 | 83 | function rulesDifference(a, b) { 84 | if (argv.verbose) { 85 | return getSortedRules( 86 | objectDifference( 87 | a.getCurrentRulesDetailed(), 88 | b.getCurrentRulesDetailed() 89 | ) 90 | ); 91 | } 92 | 93 | return getSortedRules( 94 | arrayDifference( 95 | a.getCurrentRules(), 96 | b.getCurrentRules() 97 | ) 98 | ); 99 | } 100 | 101 | process.exit(0); 102 | 103 | })().catch(/* istanbul ignore next */(e) => { 104 | console.error(e); 105 | process.exit(1); 106 | }); 107 | -------------------------------------------------------------------------------- /src/bin/find.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | const options = { 5 | getCurrentRules: ['current', 'c'], 6 | getPluginRules: ['plugin', 'p'], 7 | getAllAvailableRules: ['all-available', 'a'], 8 | getUnusedRules: ['unused', 'u'], 9 | getDeprecatedRules: ['deprecated', 'd'], 10 | n: [], 11 | error: ['error'], 12 | core: ['core'], 13 | verbose: ['verbose', 'v'], 14 | flatConfig: ['flatConfig'] 15 | }; 16 | const optionsThatError = ['getUnusedRules', 'getDeprecatedRules']; 17 | 18 | const argv = require('yargs') 19 | .boolean(Object.keys(options)) 20 | .alias(options) 21 | .option('include', { 22 | alias: 'i', 23 | choices: ['deprecated'], 24 | type: 'string' 25 | }) 26 | .option('ext', { 27 | type: 'array', 28 | default: ['.js'] 29 | }) 30 | .default('error', true) 31 | .default('core', true) 32 | .default('flatConfig', false) 33 | .argv; 34 | const getRuleURI = require('eslint-rule-documentation'); 35 | const getRuleFinder = require('../lib/rule-finder'); 36 | const cli = require('../lib/cli-util'); 37 | 38 | (async function () { 39 | 40 | const specifiedFile = argv._[0]; 41 | const finderOptions = { 42 | omitCore: !argv.core, 43 | includeDeprecated: argv.include === 'deprecated', 44 | ext: argv.ext, 45 | useFlatConfig: argv.flatConfig 46 | }; 47 | const ruleFinder = await getRuleFinder(specifiedFile, finderOptions); 48 | const errorOut = argv.error && !argv.n; 49 | let processExitCode = 0; 50 | 51 | if (!argv.c && !argv.p && !argv.a && !argv.u && !argv.d) { 52 | console.log('no option provided, please provide a valid option'); 53 | console.log('usage:'); 54 | console.log('eslint-find-rules [option] [flag]'); 55 | } else { 56 | Object.keys(options).forEach(option => { 57 | let rules; 58 | const ruleFinderMethod = ruleFinder[option]; 59 | if (argv[option] && ruleFinderMethod) { 60 | rules = ruleFinderMethod(); 61 | if (argv.verbose) { 62 | cli.push('\n' + options[option][0] + ' rules\n' + rules.length + ' rules found\n'); 63 | } 64 | if (rules.length > 0) { 65 | if (argv.verbose) { 66 | rules = rules 67 | .map(rule => [rule, getRuleURI(rule).url]) 68 | .reduce((all, single) => all.concat(single)); 69 | cli.push(rules, 2, false); 70 | } else { 71 | cli.push('\n' + options[option][0] + ' rules\n'); 72 | cli.push(rules); 73 | } 74 | if (errorOut && optionsThatError.indexOf(option) !== -1) { 75 | processExitCode = 1; 76 | } 77 | } 78 | } 79 | }); 80 | 81 | cli.write(); 82 | } 83 | process.exit(processExitCode); 84 | 85 | })().catch(/* istanbul ignore next */(e) => { 86 | console.error(e); 87 | process.exit(1); 88 | }); 89 | -------------------------------------------------------------------------------- /src/lib/array-diff.js: -------------------------------------------------------------------------------- 1 | function difference(a, b) { 2 | return a.filter(item => b.indexOf(item) === -1).sort((a, b) => a > b); 3 | } 4 | 5 | module.exports = difference; 6 | -------------------------------------------------------------------------------- /src/lib/cli-util.js: -------------------------------------------------------------------------------- 1 | const size = require('window-size'); 2 | 3 | const availableWidth = size.width || /* istanbul ignore next */ 80; 4 | const ui = require('cliui')({width: availableWidth}); 5 | 6 | function push(output, columns, uniformColWidths) { 7 | const _output = [].concat(output); 8 | 9 | const padding = {top: 0, right: 2, bottom: 0, left: 0}; 10 | const maxWidth = [_output.reduce((previous, current) => Math.max(padding.left + current.length + padding.right, previous), 0)]; 11 | 12 | const _columns = columns || Math.floor(availableWidth / maxWidth); 13 | let widths; 14 | 15 | if (uniformColWidths === false && _columns > 1) { 16 | widths = []; 17 | _output.forEach((content, index) => { 18 | widths[index % _columns] = Math.max( 19 | padding.left + content.length + padding.right, 20 | widths[index % _columns] || 0 21 | ); 22 | }); 23 | } else { 24 | widths = [Math.floor(availableWidth / _columns)]; 25 | } 26 | 27 | const cellMapper = getOutputCellMapper(widths, padding); 28 | 29 | while (_output.length) { 30 | ui.div(..._output.splice(0, _columns).map(cellMapper)); 31 | } 32 | } 33 | 34 | function write(logger) { 35 | const _logger = logger || console; 36 | const _log = _logger.log || /* istanbul ignore next */ console.log; 37 | const output = ui.toString(); 38 | 39 | // Only log when there is something to show 40 | if (output.length > 0) { 41 | _log(output); 42 | } 43 | } 44 | 45 | function getOutputCellMapper(widths, padding) { 46 | return (text, index) => { 47 | let _width = widths[index]; 48 | if (_width === undefined) { 49 | _width = widths[0]; 50 | } 51 | return { 52 | text, 53 | width: _width, 54 | padding: [padding.top, padding.right, padding.bottom, padding.left] 55 | }; 56 | }; 57 | } 58 | 59 | module.exports = { 60 | push, 61 | write 62 | }; 63 | -------------------------------------------------------------------------------- /src/lib/flatten-rules-diff.js: -------------------------------------------------------------------------------- 1 | function flattenRulesDiff(diff) { 2 | if (Array.isArray(diff)) { 3 | return flattenRulesDiffArray(diff); 4 | } else if (typeof diff === 'object') { 5 | return flattenRulesDiffObject(diff); 6 | } 7 | 8 | return []; 9 | } 10 | 11 | function flattenRulesDiffObject(diffObject) { 12 | const flattened = []; 13 | 14 | Object.keys(diffObject).forEach(ruleName => { 15 | const diff = diffObject[ruleName]; 16 | const ruleRow = [ruleName].concat( 17 | Object.keys(diff).map(configName => diff[configName]) 18 | ); 19 | 20 | flattened.push(...ruleRow); 21 | }); 22 | 23 | return flattened; 24 | } 25 | 26 | function flattenRulesDiffArray(diffArray) { 27 | const flattened = []; 28 | 29 | diffArray.forEach(diff => { 30 | flattened.push(...flattenRulesDiff(diff)); 31 | }); 32 | 33 | return flattened; 34 | } 35 | 36 | module.exports = flattenRulesDiff; 37 | -------------------------------------------------------------------------------- /src/lib/normalize-plugin-name.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file: ignore from coverage because it varies per eslint version */ 2 | 3 | let eslintNaming; 4 | 5 | function _getNormalizer() { 6 | if (eslintNaming) { 7 | return eslintNaming; 8 | } 9 | 10 | const eslintVersionFunctions = [ 11 | // eslint >= 7.8.0 12 | function () { 13 | const ESLintExports = require('eslint'); 14 | 15 | const version = ESLintExports.ESLint.version; 16 | 17 | if (!version) { 18 | throw new Error('Unable to find eslint version'); 19 | } 20 | 21 | const ESLintRC = require('@eslint/eslintrc'); 22 | 23 | const naming = ESLintRC.Legacy.naming; 24 | 25 | return { 26 | normalizePackageName: naming.normalizePackageName, 27 | getShorthandName: naming.getShorthandName 28 | } 29 | }, 30 | // eslint >= 6.1.0 31 | function () { 32 | const normalizer = require('eslint/lib/shared/naming'); 33 | 34 | return { 35 | normalizePackageName: normalizer.normalizePackageName, 36 | getShorthandName: normalizer.getShorthandName 37 | }; 38 | }, 39 | ]; 40 | 41 | for (const tryEslintVersion of eslintVersionFunctions) { 42 | try { 43 | const normalizer = tryEslintVersion(); 44 | 45 | if ( 46 | typeof normalizer.normalizePackageName === 'function' && 47 | typeof normalizer.getShorthandName === 'function' 48 | ) { 49 | eslintNaming = { 50 | normalizePackageName: normalizer.normalizePackageName, 51 | getShorthandName: normalizer.getShorthandName 52 | }; 53 | 54 | return eslintNaming; 55 | } 56 | // eslint-disable-next-line no-unused-vars, no-empty 57 | } catch (err) { 58 | } 59 | } 60 | 61 | throw new Error('eslint naming functions not found'); 62 | } 63 | 64 | function normalizePluginName(name) { 65 | const normalizer = _getNormalizer(); 66 | 67 | const module = normalizer.normalizePackageName(name, 'eslint-plugin'); 68 | const prefix = normalizer.getShorthandName(name, 'eslint-plugin'); 69 | 70 | return {module, prefix}; 71 | } 72 | 73 | module.exports = normalizePluginName; 74 | -------------------------------------------------------------------------------- /src/lib/object-diff.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | function difference(a, b) { 4 | const diff = {}; 5 | 6 | Object.keys(a).forEach(compare(diff, a, b)); 7 | Object.keys(b).forEach(compare(diff, a, b)); 8 | 9 | return diff; 10 | } 11 | 12 | function compare(diff, a, b) { 13 | return n => { 14 | if (!diff[n]) { 15 | try { 16 | assert.deepEqual(a[n], b[n]); 17 | // eslint-disable-next-line no-unused-vars 18 | } catch (err) { 19 | diff[n] = { 20 | config1: a[n], 21 | config2: b[n] 22 | }; 23 | } 24 | } 25 | }; 26 | } 27 | 28 | module.exports = difference; 29 | -------------------------------------------------------------------------------- /src/lib/rule-finder.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const { ESLint, Linter } = require('eslint'); 4 | const glob = require('glob'); 5 | const difference = require('./array-diff'); 6 | const getSortedRules = require('./sort-rules'); 7 | const normalizePluginName = require('./normalize-plugin-name'); 8 | 9 | let builtinRules, FlatESLint; 10 | try { 11 | const eslintInternal = require('eslint/use-at-your-own-risk'); 12 | builtinRules = eslintInternal.builtinRules; 13 | FlatESLint = ESLint.configType === 'flat' ? ESLint : eslintInternal.FlatESLint; 14 | // eslint-disable-next-line no-empty, no-unused-vars 15 | } catch (e) {} 16 | 17 | function _loadEslint(options, useFlatConfig) { 18 | if (!useFlatConfig) { 19 | // Ignore any config applicable depending on the location on the filesystem 20 | options.useEslintrc = false; 21 | } else if (!FlatESLint) { 22 | throw 'This version of ESLint does not support flat config.'; 23 | } 24 | 25 | return useFlatConfig 26 | ? new FlatESLint(options) 27 | : new ESLint(options); 28 | } 29 | 30 | function _getConfigFile(specifiedFile) { 31 | if (specifiedFile) { 32 | if (path.isAbsolute(specifiedFile)) { 33 | return specifiedFile; 34 | } 35 | return path.join(process.cwd(), specifiedFile); 36 | } 37 | // This is not being called with an arg. Use the package.json `main` 38 | return require(path.join(process.cwd(), 'package.json')).main; 39 | } 40 | 41 | async function _getConfigs(overrideConfigFile, files, useFlatConfig) { 42 | const esLint = _loadEslint({ 43 | // Point to the particular config 44 | overrideConfigFile 45 | }, useFlatConfig); 46 | 47 | const configs = files.map(async filePath => ( 48 | await esLint.isPathIgnored(filePath) ? false : esLint.calculateConfigForFile(filePath) 49 | )); 50 | return new Set((await Promise.all(configs)).filter(Boolean)); 51 | } 52 | 53 | async function _getConfig(configFile, files, useFlatConfig) { 54 | return Array.from(await _getConfigs(configFile, files, useFlatConfig)).reduce((prev, item) => { 55 | const plugins = useFlatConfig || ESLint.configType === 'flat' 56 | ? Object.assign({}, prev.plugins, item.plugins) 57 | : [...new Set([].concat(prev.plugins || [], item.plugins || []))] 58 | 59 | return Object.assign(prev, item, { 60 | rules: Object.assign({}, prev.rules, item.rules), 61 | plugins 62 | }); 63 | }, {}); 64 | } 65 | 66 | function _getCurrentNamesRules(config) { 67 | return config.rules ? Object.keys(config.rules) : []; 68 | } 69 | 70 | function _isDeprecated(rule) { 71 | return rule && rule.meta && rule.meta.deprecated; 72 | } 73 | 74 | function _notDeprecated(rule) { 75 | return !_isDeprecated(rule); 76 | } 77 | 78 | function _getPluginRules(config, useFlatConfig) { 79 | const pluginRules = new Map(); 80 | const plugins = config.plugins; 81 | /* istanbul ignore else */ 82 | if (plugins) { 83 | if (useFlatConfig || ESLint.configType === 'flat') { 84 | Object.entries(config.plugins) 85 | .filter(([, { rules }]) => rules) 86 | .forEach(([pluginName, { rules }]) => { 87 | Object.keys(rules).forEach(ruleName => 88 | pluginRules.set(`${pluginName}/${ruleName}`, rules[ruleName]) 89 | ); 90 | }); 91 | } else { 92 | plugins.forEach(plugin => { 93 | const normalized = normalizePluginName(plugin); 94 | const pluginConfig = require(normalized.module); 95 | if (pluginConfig.rules) { 96 | Object.keys(pluginConfig.rules).forEach(ruleName => 97 | pluginRules.set(`${normalized.prefix}/${ruleName}`, pluginConfig.rules[ruleName]) 98 | ); 99 | } 100 | }); 101 | } 102 | } 103 | return pluginRules; 104 | } 105 | 106 | function _getCoreRules() { 107 | return builtinRules || new Linter().getRules(); 108 | } 109 | 110 | function _filterRuleNames(ruleNames, rules, predicate) { 111 | return ruleNames.filter(ruleName => predicate(rules.get(ruleName))); 112 | } 113 | 114 | function _isNotCore(rule) { 115 | return rule.indexOf('/') !== '-1'; 116 | } 117 | 118 | function _escapeRegExp(str) { 119 | return str.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'); 120 | } 121 | 122 | function _createExtensionRegExp(extensions) { 123 | const normalizedExts = extensions.map(ext => _escapeRegExp( 124 | ext.startsWith('.') ? ext.slice(1) : ext 125 | )); 126 | 127 | return new RegExp(`.\\.(?:${normalizedExts.join("|")})$`); 128 | } 129 | 130 | function RuleFinder(config, {omitCore, includeDeprecated, useFlatConfig}) { 131 | let currentRuleNames = _getCurrentNamesRules(config); 132 | if (omitCore) { 133 | currentRuleNames = currentRuleNames.filter(_isNotCore); 134 | } 135 | 136 | const pluginRules = _getPluginRules(config, useFlatConfig); 137 | const coreRules = _getCoreRules(); 138 | const allRules = omitCore ? pluginRules : new Map([...coreRules, ...pluginRules]); 139 | 140 | let allRuleNames = [...allRules.keys()]; 141 | let pluginRuleNames = [...pluginRules.keys()]; 142 | if (!includeDeprecated) { 143 | allRuleNames = _filterRuleNames(allRuleNames, allRules, _notDeprecated); 144 | pluginRuleNames = _filterRuleNames(pluginRuleNames, pluginRules, _notDeprecated); 145 | } 146 | const deprecatedRuleNames = _filterRuleNames(currentRuleNames, allRules, _isDeprecated); 147 | const dedupedRuleNames = [...new Set(allRuleNames)]; 148 | const unusedRuleNames = difference(dedupedRuleNames, currentRuleNames); 149 | 150 | // Get all the current rules instead of referring the extended files or documentation 151 | this.getCurrentRules = () => getSortedRules(currentRuleNames); 152 | 153 | // Get all the current rules' particular configuration 154 | this.getCurrentRulesDetailed = () => config.rules; 155 | 156 | // Get all the plugin rules instead of referring the extended files or documentation 157 | this.getPluginRules = () => getSortedRules(pluginRuleNames); 158 | 159 | // Get all the available rules instead of referring eslint and plugin packages or documentation 160 | this.getAllAvailableRules = () => getSortedRules(dedupedRuleNames); 161 | 162 | this.getUnusedRules = () => getSortedRules(unusedRuleNames); 163 | 164 | // Get all the current rules that are deprecated 165 | this.getDeprecatedRules = () => getSortedRules(deprecatedRuleNames); 166 | } 167 | 168 | async function createRuleFinder(specifiedFile, options) { 169 | const configFile = _getConfigFile(specifiedFile); 170 | 171 | const {ext = ['.js', '.cjs', '.mjs']} = options; 172 | const extensionRegExp = _createExtensionRegExp(ext); 173 | const files = glob.sync(`**/*`, {dot: true, matchBase: true}) 174 | .filter(file => extensionRegExp.test(file)); 175 | 176 | const config = await _getConfig(configFile, files, options.useFlatConfig); 177 | 178 | return new RuleFinder(config, options); 179 | } 180 | 181 | module.exports = async function (specifiedFile, options = {}) { 182 | return createRuleFinder(specifiedFile, options); 183 | }; 184 | -------------------------------------------------------------------------------- /src/lib/sort-rules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function getSortedRules(rules) { 4 | return Array.isArray(rules) ? getSortedRulesArray(rules) : transformIntoSortedRulesArray(rules); 5 | } 6 | 7 | function getSortedRulesArray(rules) { 8 | return rules.sort(sortAlphabetically); 9 | } 10 | 11 | function transformIntoSortedRulesArray(rules) { 12 | const sortedRules = []; 13 | 14 | Object.keys(rules) 15 | .sort(sortAlphabetically) 16 | .forEach(ruleName => { 17 | const rule = {}; 18 | rule[ruleName] = rules[ruleName]; 19 | sortedRules.push(rule); 20 | }); 21 | 22 | return sortedRules; 23 | } 24 | 25 | function sortAlphabetically(a, b) { 26 | return a > b ? 1 : -1; 27 | } 28 | 29 | module.exports = getSortedRules; 30 | -------------------------------------------------------------------------------- /src/lib/stringify-rule-config.js: -------------------------------------------------------------------------------- 1 | function stringifyRuleConfig(rule) { 2 | if (typeof rule === 'string') { 3 | return rule; 4 | } else if (typeof rule === 'undefined') { 5 | return '-'; 6 | } 7 | 8 | return JSON.stringify(rule); 9 | } 10 | 11 | module.exports = stringifyRuleConfig; 12 | -------------------------------------------------------------------------------- /test/bin/diff.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const proxyquire = require('proxyquire'); 3 | const sinon = require('sinon'); 4 | 5 | const consoleLog = console.log; 6 | const processExit = process.exit; 7 | 8 | const stub = { 9 | async '../lib/rule-finder'() { 10 | return { 11 | getCurrentRules() {}, // Noop 12 | getCurrentRulesDetailed() {} // Noop 13 | }; 14 | }, 15 | '../lib/array-diff': sinon.stub().returns(['diff']), 16 | '../lib/object-diff': sinon.stub().returns([{'test-rule': {config1: 'foo-config', config2: 'bar-config'}}]) 17 | }; 18 | 19 | let exitStatus; 20 | 21 | describe('diff', () => { 22 | beforeEach(() => { 23 | process.argv = process.argv.slice(0, 2); 24 | sinon.stub(console, 'log').callsFake((...args) => { 25 | // Print out everything but the test target's output 26 | if (!args[0].match(/diff/)) { 27 | consoleLog(...args); 28 | } 29 | }); 30 | exitStatus = new Promise(resolve => { 31 | process.exit = resolve; 32 | }); 33 | }); 34 | 35 | afterEach(() => { 36 | console.log.restore(); 37 | process.exit = processExit; 38 | // purge yargs cache 39 | delete require.cache[require.resolve('yargs')]; 40 | }); 41 | 42 | it('logs diff', async () => { 43 | process.argv[2] = './foo'; 44 | process.argv[3] = './bar'; 45 | proxyquire('../../src/bin/diff', stub); 46 | assert.strictEqual(await exitStatus, 0); 47 | assert.ok( 48 | console.log.calledWith( 49 | sinon.match( 50 | /diff rules[^]*in foo but not in bar:[^]*diff[^]*in bar but not in foo:[^]*diff/ 51 | ) 52 | ) 53 | ); 54 | }); 55 | 56 | it('logs diff verbosely', async () => { 57 | process.argv[2] = '--verbose'; 58 | process.argv[3] = './foo'; 59 | process.argv[4] = './bar'; 60 | proxyquire('../../src/bin/diff', stub); 61 | assert.strictEqual(await exitStatus, 0); 62 | assert.ok( 63 | console.log.calledWith( 64 | sinon.match( 65 | /diff rules[^]*foo[^]*bar[^]*test-rule[^]*foo-config[^]*bar-config/ 66 | ) 67 | ) 68 | ); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/bin/find.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const proxyquire = require('proxyquire'); 3 | const sinon = require('sinon'); 4 | 5 | const consoleLog = console.log; 6 | const processExit = process.exit; 7 | 8 | const getCurrentRules = sinon.stub().returns(['current', 'rules']); 9 | const getPluginRules = sinon.stub().returns(['plugin', 'rules']); 10 | const getAllAvailableRules = sinon.stub().returns(['all', 'available']); 11 | const getUnusedRules = sinon.stub().returns(['unused', 'rules']); 12 | const getDeprecatedRules = sinon.stub().returns(['deprecated', 'rules']); 13 | 14 | let stub; 15 | let exitStatus; 16 | 17 | describe('bin', () => { 18 | beforeEach(() => { 19 | stub = { 20 | async '../lib/rule-finder'() { 21 | return { 22 | getCurrentRules, 23 | getPluginRules, 24 | getAllAvailableRules, 25 | getUnusedRules, 26 | getDeprecatedRules 27 | }; 28 | } 29 | }; 30 | 31 | console.log = (...args) => { 32 | if (args[0].match(/(current|plugin|all-available|unused|deprecated|rules found)/)) { 33 | return; 34 | } 35 | consoleLog(...args); 36 | }; 37 | exitStatus = new Promise(resolve => { 38 | process.exit = resolve; 39 | }); 40 | process.argv = process.argv.slice(0, 2); 41 | }); 42 | 43 | afterEach(() => { 44 | console.log = consoleLog; 45 | process.exit = processExit; 46 | // Purge yargs cache 47 | delete require.cache[require.resolve('yargs')]; 48 | }); 49 | 50 | it('no option', async () => { 51 | let callCount = 0; 52 | console.log = (...args) => { 53 | callCount += 1; 54 | if (args[0].match( 55 | /(no option provided, please provide a valid option|usage:|eslint-find-rules \[option] \[flag])/) 56 | ) { 57 | return; 58 | } 59 | consoleLog(...args); 60 | }; 61 | proxyquire('../../src/bin/find', stub); 62 | assert.strictEqual(await exitStatus, 0); 63 | assert.equal(callCount, 3); 64 | }); 65 | 66 | it('option -c|--current', async () => { 67 | process.argv[2] = '-c'; 68 | proxyquire('../../src/bin/find', stub); 69 | assert.strictEqual(await exitStatus, 0); 70 | assert.ok(getCurrentRules.called); 71 | }); 72 | 73 | it('option -p|--plugin', async () => { 74 | process.argv[2] = '-p'; 75 | proxyquire('../../src/bin/find', stub); 76 | assert.strictEqual(await exitStatus, 0); 77 | assert.ok(getPluginRules.called); 78 | }); 79 | 80 | it('option -a|--all-available', async () => { 81 | process.argv[2] = '-a'; 82 | proxyquire('../../src/bin/find', stub); 83 | assert.strictEqual(await exitStatus, 0); 84 | assert.ok(getAllAvailableRules.called); 85 | process.argv[2] = '--all-available'; 86 | proxyquire('../../src/bin/find', stub); 87 | assert.strictEqual(await exitStatus, 0); 88 | assert.ok(getAllAvailableRules.called); 89 | }); 90 | 91 | it('option -a along with --ext', async () => { 92 | process.argv[2] = '-a'; 93 | process.argv[3] = '--ext .json'; 94 | proxyquire('../../src/bin/find', stub); 95 | assert.strictEqual(await exitStatus, 0); 96 | assert.ok(getAllAvailableRules.called); 97 | }); 98 | 99 | it('option -a along with multi --ext', async () => { 100 | process.argv[2] = '-a'; 101 | process.argv[3] = '--ext .js'; 102 | process.argv[4] = '--ext .json'; 103 | proxyquire('../../src/bin/find', stub); 104 | assert.strictEqual(await exitStatus, 0); 105 | assert.ok(getAllAvailableRules.called); 106 | }); 107 | 108 | it('option -u|--unused', async () => { 109 | process.argv[2] = '-u'; 110 | proxyquire('../../src/bin/find', stub); 111 | assert.strictEqual(await exitStatus, 1); 112 | assert.ok(getUnusedRules.called); 113 | }); 114 | 115 | it('options -u|--unused and no unused rules found', async () => { 116 | getUnusedRules.returns([]); 117 | process.argv[2] = '-u'; 118 | proxyquire('../../src/bin/find', stub); 119 | assert.strictEqual(await exitStatus, 0); 120 | assert.ok(getUnusedRules.called); 121 | }); 122 | 123 | it('option -u|--unused along with -n', async () => { 124 | process.argv[2] = '-u'; 125 | process.argv[3] = '-n'; 126 | proxyquire('../../src/bin/find', stub); 127 | assert.strictEqual(await exitStatus, 0); 128 | assert.ok(getUnusedRules.called); 129 | }); 130 | 131 | it('option -u|--unused along with --no-error', async () => { 132 | process.argv[2] = '-u'; 133 | process.argv[3] = '--no-error'; 134 | proxyquire('../../src/bin/find', stub); 135 | assert.strictEqual(await exitStatus, 0); 136 | assert.ok(getUnusedRules.called); 137 | }); 138 | 139 | it('option -d|--deprecated', async () => { 140 | process.argv[2] = '-d'; 141 | proxyquire('../../src/bin/find', stub); 142 | assert.strictEqual(await exitStatus, 1); 143 | assert.ok(getDeprecatedRules.called); 144 | }); 145 | 146 | it('options -d|--deprecated and no deprecated rules found', async () => { 147 | getDeprecatedRules.returns([]); 148 | process.argv[2] = '-d'; 149 | proxyquire('../../src/bin/find', stub); 150 | assert.strictEqual(await exitStatus, 0); 151 | assert.ok(getDeprecatedRules.called); 152 | }); 153 | 154 | it('option -d|--deprecated along with -n', async () => { 155 | process.argv[2] = '-d'; 156 | process.argv[3] = '-n'; 157 | proxyquire('../../src/bin/find', stub); 158 | assert.strictEqual(await exitStatus, 0); 159 | assert.ok(getDeprecatedRules.called); 160 | }); 161 | 162 | it('option -d|--deprecated along with --no-error', async () => { 163 | process.argv[2] = '-d'; 164 | process.argv[3] = '--no-error'; 165 | proxyquire('../../src/bin/find', stub); 166 | assert.strictEqual(await exitStatus, 0); 167 | assert.ok(getDeprecatedRules.called); 168 | }); 169 | 170 | it('logs verbosely', async () => { 171 | process.argv[2] = '-c'; 172 | process.argv[3] = '-v'; 173 | proxyquire('../../src/bin/find', stub); 174 | assert.strictEqual(await exitStatus, 0); 175 | assert.ok(getCurrentRules.called); 176 | }); 177 | 178 | it('logs core rules', async () => { 179 | stub = { 180 | async '../lib/rule-finder'(specifiedFile, options) { 181 | return { 182 | getCurrentRules() { 183 | assert(!options.omitCore); 184 | return ['current', 'rules']; 185 | } 186 | }; 187 | } 188 | }; 189 | process.argv[2] = '-c'; 190 | proxyquire('../../src/bin/find', stub); 191 | assert.strictEqual(await exitStatus, 0); 192 | }); 193 | 194 | it('does not log core rules with --no-core', async () => { 195 | stub = { 196 | async '../lib/rule-finder'(specifiedFile, options) { 197 | return { 198 | getCurrentRules() { 199 | assert(options.omitCore); 200 | return ['current', 'rules']; 201 | } 202 | }; 203 | } 204 | }; 205 | process.argv[2] = '-c'; 206 | process.argv[3] = '--no-core'; 207 | proxyquire('../../src/bin/find', stub); 208 | assert.strictEqual(await exitStatus, 0); 209 | }); 210 | 211 | it('does not include deprecated rules by default', async () => { 212 | stub = { 213 | async '../lib/rule-finder'(specifiedFile, options) { 214 | return { 215 | getAllAvailableRules() { 216 | assert(!options.includeDeprecated); 217 | return ['current', 'rules']; 218 | } 219 | }; 220 | } 221 | }; 222 | process.argv[2] = '-a'; 223 | proxyquire('../../src/bin/find', stub); 224 | assert.strictEqual(await exitStatus, 0); 225 | }); 226 | 227 | it('includes deprecated rules with --include deprecated', async () => { 228 | stub = { 229 | async '../lib/rule-finder'(specifiedFile, options) { 230 | return { 231 | getAllAvailableRules() { 232 | assert(options.includeDeprecated); 233 | return ['current', 'rules']; 234 | } 235 | }; 236 | } 237 | }; 238 | process.argv[2] = '-a'; 239 | process.argv[3] = '--include=deprecated'; 240 | proxyquire('../../src/bin/find', stub); 241 | assert.strictEqual(await exitStatus, 0); 242 | }); 243 | 244 | it('includes deprecated rules with -i deprecated', async () => { 245 | stub = { 246 | async '../lib/rule-finder'(specifiedFile, options) { 247 | return { 248 | getAllAvailableRules() { 249 | assert(options.includeDeprecated); 250 | return ['current', 'rules']; 251 | } 252 | }; 253 | } 254 | }; 255 | process.argv[2] = '-a'; 256 | process.argv[3] = '-i'; 257 | process.argv[4] = 'deprecated'; 258 | proxyquire('../../src/bin/find', stub); 259 | assert.strictEqual(await exitStatus, 0); 260 | }); 261 | }); 262 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/eslint-dedupe-plugin-rules.js: -------------------------------------------------------------------------------- 1 | const eslintrc = require('./eslintrc'); 2 | const merge = require('lodash/merge'); 3 | const plugin = require('eslint-plugin-plugin'); 4 | 5 | module.exports = merge(eslintrc, { 6 | plugins: { 7 | plugin 8 | }, 9 | rules: { 10 | "plugin/duplicate-bar-rule": [2] 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/eslint-flat-config.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | files: ["**/*.js"], 4 | plugins: { 5 | plugin: { 6 | rules: { 7 | "foo-rule": {}, 8 | "old-plugin-rule": { meta: { deprecated: true } }, 9 | "bar-rule": {}, 10 | }, 11 | }, 12 | }, 13 | rules: { 14 | "foo-rule": [2], 15 | "plugin/foo-rule": [2], 16 | }, 17 | }, 18 | { 19 | files: ["**/*.json"], 20 | plugins: { 21 | jsonPlugin: { 22 | rules: { "foo-rule": {} }, 23 | }, 24 | }, 25 | rules: { 26 | "jsonPlugin/foo-rule": [2], 27 | }, 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/eslint-with-deprecated-rules.js: -------------------------------------------------------------------------------- 1 | const plugin = require('eslint-plugin-plugin'); 2 | const scopeEslintPluginScopedPlugin = require('@scope/eslint-plugin-scoped-plugin'); 3 | const scope = require('@scope/eslint-plugin'); 4 | const scopeWithDashEslintPluginScopedWithDashPlugin = require('@scope-with-dash/eslint-plugin-scoped-with-dash-plugin'); 5 | const scopeWithDash = require('@scope-with-dash/eslint-plugin'); 6 | 7 | module.exports = { 8 | plugins: { 9 | plugin, 10 | '@scope/scoped-plugin': scopeEslintPluginScopedPlugin, 11 | '@scope': scope, 12 | '@scope-with-dash/scoped-with-dash-plugin': scopeWithDashEslintPluginScopedWithDashPlugin, 13 | '@scope-with-dash': scopeWithDash 14 | }, 15 | rules: { 16 | 'foo-rule': [2], 17 | 'old-rule': [2], 18 | 19 | 'plugin/foo-rule': [2], 20 | 'plugin/old-plugin-rule': [2], 21 | 22 | '@scope/scoped-plugin/foo-rule': [2], 23 | '@scope/scoped-plugin/old-plugin-rule': [2], 24 | 25 | '@scope/foo-rule': [2], 26 | '@scope/old-plugin-rule': [2], 27 | 28 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule': [2], 29 | '@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule': [2], 30 | 31 | '@scope-with-dash/foo-rule': [2], 32 | '@scope-with-dash/old-plugin-rule': [2] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/eslint-with-overrides.js: -------------------------------------------------------------------------------- 1 | const plugin = require('eslint-plugin-plugin'); 2 | const scopeEslintPluginScopedPlugin = require('@scope/eslint-plugin-scoped-plugin'); 3 | const scope = require('@scope/eslint-plugin'); 4 | const scopeWithDashEslintPluginScopedWithDashPlugin = require('@scope-with-dash/eslint-plugin-scoped-with-dash-plugin'); 5 | const scopeWithDash = require('@scope-with-dash/eslint-plugin'); 6 | 7 | module.exports = [ 8 | { 9 | plugins: { 10 | plugin, 11 | }, 12 | rules: { 13 | "foo-rule": [2], 14 | "old-rule": [2], 15 | 16 | "plugin/old-plugin-rule": [2] 17 | }, 18 | }, 19 | { 20 | files: ["**/*.json"], 21 | plugins: { 22 | '@scope/scoped-plugin': scopeEslintPluginScopedPlugin, 23 | '@scope': scope, 24 | }, 25 | rules: { 26 | "@scope/scoped-plugin/foo-rule": [2], 27 | "@scope/foo-rule": [2] 28 | } 29 | }, 30 | { 31 | files: ["**/*.txt"], 32 | plugins: { 33 | '@scope-with-dash/scoped-with-dash-plugin': scopeWithDashEslintPluginScopedWithDashPlugin, 34 | '@scope-with-dash': scopeWithDash 35 | }, 36 | rules: { 37 | "@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule": [2] 38 | } 39 | } 40 | ]; 41 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/eslint-with-plugin-with-no-rules.js: -------------------------------------------------------------------------------- 1 | const plugin = require('eslint-plugin-plugin'); 2 | const eslintPluginNoRules = require('eslint-plugin-no-rules'); 3 | 4 | module.exports = { 5 | plugins: { 6 | plugin, 7 | 'no-rules': eslintPluginNoRules 8 | }, 9 | rules: {} 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/eslint_json.js: -------------------------------------------------------------------------------- 1 | const eslintYml = require('./eslint_yml'); 2 | const plugin = require('eslint-plugin-plugin'); 3 | const scopeEslintPluginScopedPlugin = require('@scope/eslint-plugin-scoped-plugin'); 4 | const scope = require('@scope/eslint-plugin'); 5 | const scopeWithDashEslintPluginScopedWithDashPlugin = require('@scope-with-dash/eslint-plugin-scoped-with-dash-plugin'); 6 | const scopeWithDash = require('@scope-with-dash/eslint-plugin'); 7 | 8 | module.exports = [ 9 | eslintYml, 10 | { 11 | plugins: { 12 | plugin, 13 | '@scope/scoped-plugin': scopeEslintPluginScopedPlugin, 14 | '@scope': scope, 15 | '@scope-with-dash/scoped-with-dash-plugin': scopeWithDashEslintPluginScopedWithDashPlugin, 16 | '@scope-with-dash': scopeWithDash 17 | }, 18 | rules: { 19 | '@scope/scoped-plugin/foo-rule': [2], 20 | '@scope/foo-rule': [2], 21 | 22 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule': [2], 23 | '@scope-with-dash/foo-rule': [2] 24 | } 25 | } 26 | ]; 27 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/eslint_yml.js: -------------------------------------------------------------------------------- 1 | const eslintrc = require('./eslintrc'); 2 | const merge = require('lodash/merge'); 3 | 4 | module.exports = merge(eslintrc, { 5 | rules: { 6 | 'bar-rule': [2] 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | "foo-rule": [2] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/no-path/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'foo-rule': [2] 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/post-v8/no-path/index.txt: -------------------------------------------------------------------------------- 1 | some files -------------------------------------------------------------------------------- /test/fixtures/post-v8/no-path/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fake-project", 3 | "version": "0.0.0", 4 | "description": "Simulating a project's package.json.", 5 | "private": true, 6 | "main": "./index.js" 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/eslint-dedupe-plugin-rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./eslintrc", 3 | "plugins": [ 4 | "plugin" 5 | ], 6 | "rules": { 7 | "plugin/duplicate-bar-rule": [2] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/eslint-flat-config.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | files: ["**/*.js"], 4 | plugins: { 5 | plugin: { 6 | rules: { 7 | "foo-rule": {}, 8 | "old-plugin-rule": { meta: { deprecated: true } }, 9 | "bar-rule": {}, 10 | }, 11 | }, 12 | }, 13 | rules: { 14 | "foo-rule": [2], 15 | "plugin/foo-rule": [2], 16 | }, 17 | }, 18 | { 19 | files: ["**/*.json"], 20 | plugins: { 21 | jsonPlugin: { 22 | rules: { "foo-rule": {} }, 23 | }, 24 | }, 25 | rules: { 26 | "jsonPlugin/foo-rule": [2], 27 | }, 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/eslint-with-deprecated-rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "plugin", 4 | "@scope/scoped-plugin", 5 | "@scope", 6 | "@scope-with-dash/scoped-with-dash-plugin", 7 | "@scope-with-dash/eslint-plugin" 8 | ], 9 | "rules": { 10 | "foo-rule": [2], 11 | "old-rule": [2], 12 | 13 | "plugin/foo-rule": [2], 14 | "plugin/old-plugin-rule": [2], 15 | 16 | "@scope/scoped-plugin/foo-rule": [2], 17 | "@scope/scoped-plugin/old-plugin-rule": [2], 18 | 19 | "@scope/foo-rule": [2], 20 | "@scope/old-plugin-rule": [2], 21 | 22 | "@scope-with-dash/scoped-with-dash-plugin/foo-rule": [2], 23 | "@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule": [2], 24 | 25 | "@scope-with-dash/foo-rule": [2], 26 | "@scope-with-dash/old-plugin-rule": [2] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/eslint-with-overrides.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "plugin" 4 | ], 5 | "rules": { 6 | "foo-rule": [2], 7 | "old-rule": [2], 8 | 9 | "plugin/old-plugin-rule": [2] 10 | }, 11 | 12 | "overrides": [ 13 | { 14 | "files": ["**/*.json"], 15 | 16 | "plugins": [ 17 | "@scope/scoped-plugin", 18 | "@scope" 19 | ], 20 | "rules": { 21 | "@scope/scoped-plugin/foo-rule": [2], 22 | "@scope/foo-rule": [2] 23 | } 24 | }, 25 | { 26 | "files": ["**/*.txt"], 27 | 28 | "plugins": [ 29 | "@scope-with-dash/scoped-with-dash-plugin", 30 | "@scope-with-dash/eslint-plugin" 31 | ], 32 | "rules": { 33 | "@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule": [2] 34 | } 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/eslint-with-plugin-with-no-rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "plugin", 4 | "no-rules" 5 | ], 6 | "rules": {} 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./eslint.yml", 3 | "plugins": [ 4 | "plugin", 5 | "@scope/scoped-plugin", 6 | "@scope", 7 | "@scope-with-dash/scoped-with-dash-plugin", 8 | "@scope-with-dash/eslint-plugin" 9 | ], 10 | "rules": { 11 | "@scope/scoped-plugin/foo-rule": [2], 12 | "@scope/foo-rule": [2], 13 | 14 | "@scope-with-dash/scoped-with-dash-plugin/foo-rule": [2], 15 | "@scope-with-dash/foo-rule": [2] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/eslint.yml: -------------------------------------------------------------------------------- 1 | extends: 2 | ./eslintrc 3 | rules: 4 | bar-rule: 5 | - 2 6 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "foo-rule": [2] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/no-path/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'foo-rule': [2] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/prior-v8/no-path/index.txt: -------------------------------------------------------------------------------- 1 | some files -------------------------------------------------------------------------------- /test/fixtures/prior-v8/no-path/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fake-project", 3 | "version": "0.0.0", 4 | "description": "Simulating a project's package.json.", 5 | "private": true, 6 | "main": "./index.js" 7 | } 8 | -------------------------------------------------------------------------------- /test/lib/array-diff.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const difference = require('../../src/lib/array-diff'); 3 | 4 | describe('array difference', () => { 5 | it('should return difference', () => { 6 | assert.deepEqual( 7 | difference(['a', 'b', 'c'], ['x', 'y', 'z']), 8 | ['a', 'b', 'c'] 9 | ); 10 | assert.deepEqual( 11 | difference(['a', 'b', 'c'], ['a', 'y', 'z']), 12 | ['b', 'c'] 13 | ); 14 | assert.deepEqual( 15 | difference(['a', 'b', 'c'], ['a', 'b', 'z']), 16 | ['c'] 17 | ); 18 | assert.deepEqual( 19 | difference(['a', 'b', 'c'], ['a', 'b', 'c']), 20 | [] 21 | ); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/lib/cli-util.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const proxyquire = require('proxyquire'); 3 | const sinon = require('sinon'); 4 | 5 | const moduleStub = { 6 | 'window-size': {width: 80} 7 | }; 8 | 9 | const loggerStub = { 10 | log() {} // Noop 11 | }; 12 | 13 | describe('cli-util', () => { 14 | before(() => { 15 | sinon.stub(loggerStub, 'log'); 16 | }); 17 | 18 | after(() => { 19 | loggerStub.log.restore(); 20 | }); 21 | 22 | it('does not print empty lines', () => { 23 | const cli = proxyquire('../../src/lib/cli-util', moduleStub); 24 | 25 | // Nothing pushed to cli 26 | cli.write(loggerStub); 27 | 28 | assert.ok( 29 | loggerStub.log.called === false 30 | ); 31 | }); 32 | 33 | it('prints out single lines', () => { 34 | const cli = proxyquire('../../src/lib/cli-util', moduleStub); 35 | 36 | cli.push('A single line'); 37 | cli.write(loggerStub); 38 | 39 | assert.ok( 40 | loggerStub.log.calledWith( 41 | sinon.match(/^A single line$/) 42 | ) 43 | ); 44 | }); 45 | 46 | it('prints out multiple columns', () => { 47 | const cli = proxyquire('../../src/lib/cli-util', moduleStub); 48 | 49 | cli.push([ 50 | 'Everything in a single cell', 51 | 'Everything in a single cell', 52 | 'Everything in a single cell', 53 | 'Everything in a single cell', 54 | 'Everything in a single cell' 55 | ]); 56 | cli.write(loggerStub); 57 | 58 | assert.ok( 59 | loggerStub.log.calledWith( 60 | sinon.match(/^(Everything in a single cell\s{13}Everything in a single cell\n){2}Everything in a single cell$/) 61 | ) 62 | ); 63 | }); 64 | 65 | it('prints out with an exact amount of columns', () => { 66 | const cli = proxyquire('../../src/lib/cli-util', moduleStub); 67 | 68 | cli.push([ 69 | 'This in the first row', 70 | 'This in the first row', 71 | 'This in the first row', 72 | 'This in the second row', 73 | 'This in the second row' 74 | ], 3); 75 | cli.write(loggerStub); 76 | 77 | assert.ok( 78 | loggerStub.log.calledWith( 79 | sinon.match( 80 | /^(This in the first row(\s{5}){0,1}){3}\n(This in the second row(\s{4}){0,1}){2}$/ 81 | ) 82 | ) 83 | ); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /test/lib/flatten-rules-diff.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flattenRulesDiff = require('../../src/lib/flatten-rules-diff'); 3 | 4 | describe('flatten rules diff', () => { 5 | it('should return flat array from diff-object with single rule', () => { 6 | assert.deepEqual( 7 | flattenRulesDiff({'foo-rule': {config1: [2, 'foo'], config2: [2, 'bar']}}), 8 | ['foo-rule', [2, 'foo'], [2, 'bar']] 9 | ); 10 | }); 11 | 12 | it('should return flat array from diff-object with multiple rules', () => { 13 | assert.deepEqual( 14 | flattenRulesDiff({ 15 | 'foo-rule': {config1: [2, 'foo'], config2: [2, 'bar']}, 16 | 'bar-rule': {config1: undefined, config2: [1, 'bar']} 17 | }), 18 | ['foo-rule', [2, 'foo'], [2, 'bar'], 'bar-rule', undefined, [1, 'bar']] 19 | ); 20 | }); 21 | 22 | it('should return flat array from an array of diff-objects', () => { 23 | assert.deepEqual( 24 | flattenRulesDiff([ 25 | {'foo-rule': {config1: [2, 'foo'], config2: [2, 'bar']}}, 26 | {'bar-rule': {config1: undefined, config2: [1, 'bar']}} 27 | ]), 28 | ['foo-rule', [2, 'foo'], [2, 'bar'], 'bar-rule', undefined, [1, 'bar']] 29 | ); 30 | }); 31 | 32 | it('should return empty array on anything else', () => { 33 | assert.deepEqual( 34 | flattenRulesDiff(undefined), 35 | [] 36 | ); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/lib/object-diff.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const difference = require('../../src/lib/object-diff'); 3 | 4 | describe('object difference', () => { 5 | it('should return difference', () => { 6 | assert.deepEqual( 7 | difference({'foo-rule': [2, 'foo']}, {'foo-rule': [2, 'bar']}), 8 | {'foo-rule': {config1: [2, 'foo'], config2: [2, 'bar']}} 9 | ); 10 | assert.deepEqual( 11 | difference({'foo-rule': [2, 'foo', 'bar']}, {'foo-rule': 2}), 12 | {'foo-rule': {config1: [2, 'foo', 'bar'], config2: 2}} 13 | ); 14 | assert.deepEqual( 15 | difference({'foo-rule': [0, 'foo']}, {'bar-rule': [1, 'bar']}), 16 | { 17 | 'foo-rule': {config1: [0, 'foo'], config2: undefined}, 18 | 'bar-rule': {config1: undefined, config2: [1, 'bar']} 19 | } 20 | ); 21 | assert.deepEqual( 22 | difference({'foo-rule': [1, 'foo', 'bar']}, {'foo-rule': [1, 'foo', 'bar']}), 23 | {} 24 | ); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/lib/rule-finder.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const createRequire = require('create-require'); 3 | const assert = require('assert'); 4 | const proxyquire = require('proxyquire'); 5 | const { builtinRules } = require('eslint/use-at-your-own-risk'); 6 | const semver = require('semver'); 7 | const merge = require('lodash.merge'); 8 | const { ESLint } = require('eslint'); 9 | 10 | const processCwd = process.cwd; 11 | const isV8Eslint = ESLint.configType === 'eslintrc'; 12 | const eslintVersion = isV8Eslint ? 'prior-v8' : 'post-v8'; 13 | 14 | const mockCreateRequire = (getExport, plugins, relative) => { 15 | // Use the mocked require. 16 | const moduleRequire = (id) => { 17 | const targetExport = getExport(); 18 | return module.children 19 | .find((m) => m.exports === targetExport) 20 | .require(id); 21 | }; 22 | return Object.assign(moduleRequire, { 23 | // The strategy is simple: if called with one of our plugins, just return 24 | // the module name, as-is. This is a lie because what we return is not a 25 | // path, but it is simple, and works. Otherwise, we just call the original 26 | // `resolve` from the stock module. 27 | resolve: (name) => ( 28 | plugins.includes(name) ? name : createRequire(relative).resolve(name) 29 | ) 30 | }); 31 | }; 32 | 33 | // https://github.com/eslint/eslint/blob/main/lib/config/default-config.js 34 | const defaultConfig = [ 35 | { 36 | plugins: { 37 | "@": { 38 | languages: { 39 | js: null 40 | }, 41 | rules: Object.keys(builtinRules).map((ruleId) => { 42 | return { [ruleId]: ruleId() }; 43 | }) 44 | } 45 | }, 46 | language: "@/js", 47 | languageOptions: { 48 | sourceType: "module", 49 | ecmaVersion: "latest", 50 | parser: null, // require("espree"), 51 | parserOptions: {} 52 | }, 53 | linterOptions: { 54 | reportUnusedDisableDirectives: 1 55 | } 56 | }, 57 | { 58 | ignores: [ 59 | "**/node_modules/", 60 | ".git/" 61 | ] 62 | }, 63 | { 64 | files: ["**/*.js", "**/*.mjs"] 65 | }, 66 | { 67 | files: ["**/*.cjs"], 68 | languageOptions: { 69 | sourceType: "commonjs", 70 | ecmaVersion: "latest" 71 | } 72 | } 73 | ]; 74 | 75 | const mockedBuiltinRules = new Map() 76 | .set('foo-rule', {}) 77 | .set('old-rule', { meta: { deprecated: true } }) 78 | .set('bar-rule', {}) 79 | .set('baz-rule', {}) 80 | 81 | const mock = { 82 | 'eslint-plugin-plugin': { 83 | rules: { 84 | 'foo-rule': {}, 85 | 'bar-rule': {}, 86 | 'old-plugin-rule': {meta: {deprecated: true}}, 87 | 'baz-rule': {} 88 | }, 89 | '@noCallThru': true, 90 | '@global': true 91 | }, 92 | 'eslint-plugin-no-rules': { 93 | processors: {}, 94 | '@noCallThru': true, 95 | '@global': true 96 | }, 97 | '@scope/eslint-plugin-scoped-plugin': { 98 | rules: { 99 | 'foo-rule': {}, 100 | 'old-plugin-rule': {meta: {deprecated: true}}, 101 | 'bar-rule': {} 102 | }, 103 | '@noCallThru': true, 104 | '@global': true 105 | }, 106 | '@scope/eslint-plugin': { 107 | rules: { 108 | 'foo-rule': {}, 109 | 'old-plugin-rule': {meta: {deprecated: true}}, 110 | 'bar-rule': {} 111 | }, 112 | '@noCallThru': true, 113 | '@global': true 114 | }, 115 | '@scope-with-dash/eslint-plugin-scoped-with-dash-plugin': { 116 | rules: { 117 | 'foo-rule': {}, 118 | 'old-plugin-rule': {meta: {deprecated: true}}, 119 | 'bar-rule': {} 120 | }, 121 | '@noCallThru': true, 122 | '@global': true 123 | }, 124 | '@scope-with-dash/eslint-plugin': { 125 | rules: { 126 | 'foo-rule': {}, 127 | 'old-plugin-rule': {meta: {deprecated: true}}, 128 | 'bar-rule': {} 129 | }, 130 | '@noCallThru': true, 131 | '@global': true 132 | } 133 | }; 134 | 135 | const mockedDedupedBuiltinRules = new Map() 136 | .set('foo-rule', {}) 137 | .set('bar-rule', {}) 138 | .set('plugin/duplicate-foo-rule', {}) 139 | .set('plugin/duplicate-bar-rule', {}) 140 | 141 | const mockForDedupeTests = { 142 | 'eslint-plugin-plugin': { 143 | rules: { 144 | 'duplicate-foo-rule': {}, 145 | 'duplicate-bar-rule': {} 146 | }, 147 | '@noCallThru': true, 148 | '@global': true 149 | } 150 | }; 151 | 152 | const getRuleFinder = isV8Eslint 153 | ? proxyquire('../../src/lib/rule-finder', { 154 | eslint: { 155 | Linter: class { 156 | getRules() { 157 | return mockedBuiltinRules 158 | } 159 | }, 160 | }, 161 | "eslint/use-at-your-own-risk": { 162 | builtinRules: mockedBuiltinRules 163 | }, 164 | "../rules": { 165 | get(id) { 166 | return mockedBuiltinRules.get(id) 167 | }, 168 | '@global': true 169 | }, 170 | module: { 171 | createRequire: (relative) => mockCreateRequire( 172 | () => getRuleFinder, 173 | [ 174 | 'eslint-plugin-plugin', 175 | 'eslint-plugin-no-rules', 176 | '@scope/eslint-plugin-scoped-plugin', 177 | '@scope/eslint-plugin', 178 | '@scope-with-dash/eslint-plugin-scoped-with-dash-plugin', 179 | '@scope-with-dash/eslint-plugin' 180 | ], 181 | relative 182 | ), 183 | '@global': true 184 | }, 185 | ...mock 186 | }) 187 | : (specifiedFileRelative, options) => { 188 | const config = specifiedFileRelative 189 | ? proxyquire(path.resolve(specifiedFileRelative), mock) 190 | : proxyquire(path.resolve(process.cwd(), require(path.join(process.cwd(), 'package.json')).main), mock); 191 | 192 | const ruleFinder = proxyquire('../../src/lib/rule-finder', { 193 | eslint: { 194 | // for v9 ESLint 195 | ESLint: class { 196 | // https://github.com/eslint/eslint/blob/v9.14.0/lib/eslint/eslint.js#L434 197 | static configType = 'flat'; 198 | // Mock to validate with rules that are not in ESLint Mock to validate with rules that are not in ESLint, 199 | // or else you will get an error that the rule does not exist in ESLint 200 | isPathIgnored(filePath) { 201 | return filePath.includes('.ts') ? true : false; 202 | } 203 | // Mock to validate with rules that are not in ESLint Mock to validate with rules that are not in ESLint, 204 | // or else you will get an error that the rule does not exist in ESLint 205 | // eslint-disable-next-line no-unused-vars 206 | calculateConfigForFile(filePath) { 207 | const mergedConfig = {}; 208 | defaultConfig.forEach(config => { 209 | merge(mergedConfig, config); 210 | }); 211 | 212 | return Array.isArray(config) 213 | ? config.reduce((acc, cfg) => merge(acc, cfg), mergedConfig) 214 | : merge(mergedConfig, config) 215 | } 216 | }, 217 | }, 218 | 'eslint/use-at-your-own-risk': { 219 | builtinRules: mockedBuiltinRules 220 | }, 221 | }); 222 | return ruleFinder(specifiedFileRelative, options); 223 | }; 224 | 225 | const getRuleFinderForDedupeTests = isV8Eslint 226 | ? proxyquire('../../src/lib/rule-finder', { 227 | eslint: { 228 | Linter: class { 229 | getRules() { 230 | return mockedDedupedBuiltinRules 231 | } 232 | }, 233 | }, 234 | "eslint/use-at-your-own-risk": { 235 | builtinRules: mockedDedupedBuiltinRules 236 | }, 237 | "../rules": { 238 | get(id) { 239 | return mockedDedupedBuiltinRules.get(id) 240 | }, 241 | '@global': true 242 | }, 243 | module: { 244 | createRequire: (relative) => mockCreateRequire( 245 | () => getRuleFinderForDedupeTests, 246 | [ 247 | 'eslint-plugin-plugin' 248 | ], 249 | relative 250 | ), 251 | '@global': true 252 | }, 253 | ...mockForDedupeTests 254 | }) 255 | : (specifiedFileRelative, options) => { 256 | const config = specifiedFileRelative 257 | ? proxyquire(path.resolve(specifiedFileRelative), mockForDedupeTests) 258 | : proxyquire(path.resolve(process.cwd(), require(path.join(process.cwd(), 'package.json')).main), mockForDedupeTests); 259 | 260 | const ruleFinder = proxyquire('../../src/lib/rule-finder', { 261 | eslint: { 262 | ESLint: class { 263 | static configType = 'flat'; 264 | isPathIgnored(filePath) { 265 | return filePath.includes('.ts') ? true : false; 266 | } 267 | // eslint-disable-next-line no-unused-vars 268 | calculateConfigForFile(filePath) { 269 | const mergedConfig = {}; 270 | defaultConfig.forEach(config => { 271 | merge(mergedConfig, config); 272 | }); 273 | 274 | return Array.isArray(config) 275 | ? config.reduce((acc, cfg) => merge(acc, cfg), mergedConfig) 276 | : merge(mergedConfig, config) 277 | } 278 | }, 279 | }, 280 | 'eslint/use-at-your-own-risk': { 281 | builtinRules: mockedDedupedBuiltinRules 282 | }, 283 | }); 284 | return ruleFinder(specifiedFileRelative, options); 285 | }; 286 | 287 | const getRuleFinderNoFlatSupport = proxyquire('../../src/lib/rule-finder', { 288 | eslint: { 289 | Linter: class { 290 | getRules() { 291 | return mockedBuiltinRules 292 | } 293 | }, 294 | }, 295 | 'eslint/use-at-your-own-risk': { 296 | FlatESLint: undefined 297 | } 298 | }); 299 | 300 | const noSpecifiedFile = path.resolve(process.cwd(), `./test/fixtures/${eslintVersion}/no-path`); 301 | const specifiedFileRelative = `./test/fixtures/${eslintVersion}/${isV8Eslint ? 'eslint.json' : 'eslint_json.js'}`; 302 | const specifiedFileAbsolute = path.join(process.cwd(), specifiedFileRelative); 303 | const noRulesFile = path.join(process.cwd(), `./test/fixtures/${eslintVersion}/eslint-with-plugin-with-no-rules.${isV8Eslint ? 'json' : 'js'}`); 304 | const noDuplicateRulesFiles = `./test/fixtures/${eslintVersion}/eslint-dedupe-plugin-rules.${isV8Eslint ? 'json' : 'js'}`; 305 | const usingDeprecatedRulesFile = path.join(process.cwd(), `./test/fixtures/${eslintVersion}/eslint-with-deprecated-rules.${isV8Eslint ? 'json' : 'js'}`); 306 | const usingWithOverridesFile = path.join(process.cwd(), `./test/fixtures/${eslintVersion}/eslint-with-overrides.${isV8Eslint ? 'json' : 'js'}`); 307 | const specifiedFlatConfigFileRelative = `./test/fixtures/${eslintVersion}/eslint-flat-config.js`; 308 | 309 | describe('rule-finder', function() { 310 | // increase timeout because proxyquire adds a significant delay 311 | this.timeout(semver.satisfies(process.version, '> 10') ? 5e3 : (semver.satisfies(process.version, '> 4') ? 20e3 : 30e3)); 312 | 313 | afterEach(() => { 314 | process.cwd = processCwd; 315 | }); 316 | 317 | it('no specifiedFile - unused rules', async () => { 318 | process.cwd = function () { 319 | return noSpecifiedFile; 320 | }; 321 | const ruleFinder = await getRuleFinder(); 322 | assert.deepEqual(ruleFinder.getUnusedRules(), ['bar-rule', 'baz-rule']); 323 | }); 324 | 325 | it('no specifiedFile - unused rules including deprecated', async () => { 326 | process.cwd = function () { 327 | return noSpecifiedFile; 328 | }; 329 | const ruleFinder = await getRuleFinder(null, {includeDeprecated: true}); 330 | assert.deepEqual(ruleFinder.getUnusedRules(), ['bar-rule', 'baz-rule', 'old-rule']); 331 | }); 332 | 333 | it('no specifiedFile - current rules', async () => { 334 | process.cwd = function () { 335 | return noSpecifiedFile; 336 | }; 337 | const ruleFinder = await getRuleFinder(); 338 | assert.deepEqual(ruleFinder.getCurrentRules(), ['foo-rule']); 339 | }); 340 | 341 | it('no specifiedFile - current rule config', async () => { 342 | process.cwd = function () { 343 | return noSpecifiedFile; 344 | }; 345 | const ruleFinder = await getRuleFinder(); 346 | assert.deepEqual(ruleFinder.getCurrentRulesDetailed(), {'foo-rule': [2]}); 347 | }); 348 | 349 | it('no specifiedFile - plugin rules', async () => { 350 | process.cwd = function () { 351 | return noSpecifiedFile; 352 | }; 353 | const ruleFinder = await getRuleFinder(); 354 | assert.deepEqual(ruleFinder.getPluginRules(), []); 355 | }); 356 | 357 | it('no specifiedFile - all available rules', async () => { 358 | process.cwd = function () { 359 | return noSpecifiedFile; 360 | }; 361 | const ruleFinder = await getRuleFinder(); 362 | assert.deepEqual(ruleFinder.getAllAvailableRules(), ['bar-rule', 'baz-rule', 'foo-rule']); 363 | }); 364 | 365 | it('no specifiedFile - all available rules without core', async () => { 366 | process.cwd = function () { 367 | return noSpecifiedFile; 368 | }; 369 | const ruleFinder = await getRuleFinder(null, {omitCore: true}); 370 | assert.deepEqual(ruleFinder.getAllAvailableRules(), []); 371 | }); 372 | 373 | it('no specifiedFile - all available rules including deprecated', async () => { 374 | process.cwd = function () { 375 | return noSpecifiedFile; 376 | }; 377 | const ruleFinder = await getRuleFinder(null, {includeDeprecated: true}); 378 | assert.deepEqual(ruleFinder.getAllAvailableRules(), ['bar-rule', 'baz-rule', 'foo-rule', 'old-rule']); 379 | }); 380 | 381 | it('specifiedFile (relative path) - unused rules', async () => { 382 | const ruleFinder = await getRuleFinder(specifiedFileRelative); 383 | assert.deepEqual(ruleFinder.getUnusedRules(), [ 384 | '@scope-with-dash/bar-rule', 385 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 386 | '@scope/bar-rule', 387 | '@scope/scoped-plugin/bar-rule', 388 | 'baz-rule', 389 | 'plugin/bar-rule', 390 | 'plugin/baz-rule', 391 | 'plugin/foo-rule' 392 | ]); 393 | }); 394 | 395 | it('specifiedFile (relative path) - unused rules including deprecated', async () => { 396 | const ruleFinder = await getRuleFinder(specifiedFileRelative, {includeDeprecated: true}); 397 | assert.deepEqual(ruleFinder.getUnusedRules(), [ 398 | '@scope-with-dash/bar-rule', 399 | '@scope-with-dash/old-plugin-rule', 400 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 401 | '@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule', 402 | '@scope/bar-rule', 403 | '@scope/old-plugin-rule', 404 | '@scope/scoped-plugin/bar-rule', 405 | '@scope/scoped-plugin/old-plugin-rule', 406 | 'baz-rule', 407 | 'old-rule', 408 | 'plugin/bar-rule', 409 | 'plugin/baz-rule', 410 | 'plugin/foo-rule', 411 | 'plugin/old-plugin-rule' 412 | ]); 413 | }); 414 | 415 | it('specifiedFile (relative path) - current rules', async () => { 416 | const ruleFinder = await getRuleFinder(specifiedFileRelative); 417 | assert.deepEqual(ruleFinder.getCurrentRules(), [ 418 | '@scope-with-dash/foo-rule', 419 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 420 | '@scope/foo-rule', 421 | '@scope/scoped-plugin/foo-rule', 422 | 'bar-rule', 423 | 'foo-rule' 424 | ]); 425 | }); 426 | 427 | it('specifiedFile (relative path) - current rules with ext', async () => { 428 | const ruleFinder = await getRuleFinder(specifiedFileRelative, { ext: ['.json'] }); 429 | assert.deepEqual(ruleFinder.getCurrentRules(), [ 430 | '@scope-with-dash/foo-rule', 431 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 432 | '@scope/foo-rule', 433 | '@scope/scoped-plugin/foo-rule', 434 | 'bar-rule', 435 | 'foo-rule' 436 | ]); 437 | }); 438 | 439 | it('specifiedFile (relative path) - current rules with ext without dot', async () => { 440 | const ruleFinder = await getRuleFinder(specifiedFileRelative, { ext: ['json'] }); 441 | assert.deepEqual(ruleFinder.getCurrentRules(), [ 442 | '@scope-with-dash/foo-rule', 443 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 444 | '@scope/foo-rule', 445 | '@scope/scoped-plugin/foo-rule', 446 | 'bar-rule', 447 | 'foo-rule' 448 | ]); 449 | }); 450 | 451 | it('specifiedFile (relative path) - current rules with ext not found', async () => { 452 | const ruleFinder = await getRuleFinder(specifiedFileRelative, { ext: ['.ts'] }); 453 | assert.deepEqual(ruleFinder.getCurrentRules(), []); 454 | }); 455 | 456 | it('specifiedFile (relative path) - current rule config', async () => { 457 | const ruleFinder = await getRuleFinder(specifiedFileRelative); 458 | assert.deepEqual(ruleFinder.getCurrentRulesDetailed(), { 459 | '@scope-with-dash/foo-rule': [2], 460 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule': [2], 461 | '@scope/foo-rule': [2], 462 | '@scope/scoped-plugin/foo-rule': [2], 463 | 'bar-rule': [2], 464 | 'foo-rule': [2] 465 | }); 466 | }); 467 | 468 | it('specifiedFile (relative path) - plugin rules', async () => { 469 | const ruleFinder = await getRuleFinder(specifiedFileRelative); 470 | assert.deepEqual(ruleFinder.getPluginRules(), [ 471 | '@scope-with-dash/bar-rule', 472 | '@scope-with-dash/foo-rule', 473 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 474 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 475 | '@scope/bar-rule', 476 | '@scope/foo-rule', 477 | '@scope/scoped-plugin/bar-rule', 478 | '@scope/scoped-plugin/foo-rule', 479 | 'plugin/bar-rule', 480 | 'plugin/baz-rule', 481 | 'plugin/foo-rule' 482 | ]); 483 | }); 484 | 485 | it('specifiedFile (relative path) - plugin rules including deprecated', async () => { 486 | const ruleFinder = await getRuleFinder(specifiedFileRelative, {includeDeprecated: true}); 487 | assert.deepEqual(ruleFinder.getPluginRules(), [ 488 | '@scope-with-dash/bar-rule', 489 | '@scope-with-dash/foo-rule', 490 | '@scope-with-dash/old-plugin-rule', 491 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 492 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 493 | '@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule', 494 | '@scope/bar-rule', 495 | '@scope/foo-rule', 496 | '@scope/old-plugin-rule', 497 | '@scope/scoped-plugin/bar-rule', 498 | '@scope/scoped-plugin/foo-rule', 499 | '@scope/scoped-plugin/old-plugin-rule', 500 | 'plugin/bar-rule', 501 | 'plugin/baz-rule', 502 | 'plugin/foo-rule', 503 | 'plugin/old-plugin-rule' 504 | ]); 505 | }); 506 | 507 | it('specifiedFile (relative path) - all available rules', async () => { 508 | const ruleFinder = await getRuleFinder(specifiedFileRelative); 509 | assert.deepEqual( 510 | ruleFinder.getAllAvailableRules(), 511 | [ 512 | '@scope-with-dash/bar-rule', 513 | '@scope-with-dash/foo-rule', 514 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 515 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 516 | '@scope/bar-rule', 517 | '@scope/foo-rule', 518 | '@scope/scoped-plugin/bar-rule', 519 | '@scope/scoped-plugin/foo-rule', 520 | 'bar-rule', 521 | 'baz-rule', 522 | 'foo-rule', 523 | 'plugin/bar-rule', 524 | 'plugin/baz-rule', 525 | 'plugin/foo-rule' 526 | ] 527 | ); 528 | }); 529 | 530 | it('specifiedFile (relative path) - all available rules without core', async () => { 531 | const ruleFinder = await getRuleFinder(specifiedFileRelative, {omitCore: true}); 532 | assert.deepEqual( 533 | ruleFinder.getAllAvailableRules(), 534 | [ 535 | '@scope-with-dash/bar-rule', 536 | '@scope-with-dash/foo-rule', 537 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 538 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 539 | '@scope/bar-rule', 540 | '@scope/foo-rule', 541 | '@scope/scoped-plugin/bar-rule', 542 | '@scope/scoped-plugin/foo-rule', 543 | 'plugin/bar-rule', 544 | 'plugin/baz-rule', 545 | 'plugin/foo-rule' 546 | ] 547 | ); 548 | }); 549 | 550 | it('specifiedFile (relative path) - all available rules including deprecated', async () => { 551 | const ruleFinder = await getRuleFinder(specifiedFileRelative, {includeDeprecated: true}); 552 | assert.deepEqual( 553 | ruleFinder.getAllAvailableRules(), 554 | [ 555 | '@scope-with-dash/bar-rule', 556 | '@scope-with-dash/foo-rule', 557 | '@scope-with-dash/old-plugin-rule', 558 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 559 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 560 | '@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule', 561 | '@scope/bar-rule', 562 | '@scope/foo-rule', 563 | '@scope/old-plugin-rule', 564 | '@scope/scoped-plugin/bar-rule', 565 | '@scope/scoped-plugin/foo-rule', 566 | '@scope/scoped-plugin/old-plugin-rule', 567 | 'bar-rule', 568 | 'baz-rule', 569 | 'foo-rule', 570 | 'old-rule', 571 | 'plugin/bar-rule', 572 | 'plugin/baz-rule', 573 | 'plugin/foo-rule', 574 | 'plugin/old-plugin-rule' 575 | ] 576 | ); 577 | }); 578 | 579 | it('specifiedFile (absolute path) - unused rules', async () => { 580 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute); 581 | assert.deepEqual(ruleFinder.getUnusedRules(), [ 582 | '@scope-with-dash/bar-rule', 583 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 584 | '@scope/bar-rule', 585 | '@scope/scoped-plugin/bar-rule', 586 | 'baz-rule', 587 | 'plugin/bar-rule', 588 | 'plugin/baz-rule', 589 | 'plugin/foo-rule' 590 | ]); 591 | }); 592 | 593 | it('specifiedFile (absolute path) - unused rules', async () => { 594 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute, {includeDeprecated: true}); 595 | assert.deepEqual(ruleFinder.getUnusedRules(), [ 596 | '@scope-with-dash/bar-rule', 597 | '@scope-with-dash/old-plugin-rule', 598 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 599 | '@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule', 600 | '@scope/bar-rule', 601 | '@scope/old-plugin-rule', 602 | '@scope/scoped-plugin/bar-rule', 603 | '@scope/scoped-plugin/old-plugin-rule', 604 | 'baz-rule', 605 | 'old-rule', 606 | 'plugin/bar-rule', 607 | 'plugin/baz-rule', 608 | 'plugin/foo-rule', 609 | 'plugin/old-plugin-rule' 610 | ]); 611 | }); 612 | 613 | it('specifiedFile (absolute path) - current rules', async () => { 614 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute); 615 | assert.deepEqual(ruleFinder.getCurrentRules(), [ 616 | '@scope-with-dash/foo-rule', 617 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 618 | '@scope/foo-rule', 619 | '@scope/scoped-plugin/foo-rule', 620 | 'bar-rule', 621 | 'foo-rule' 622 | ]); 623 | }); 624 | 625 | it('specifiedFile (absolute path) - current rule config', async () => { 626 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute); 627 | assert.deepEqual(ruleFinder.getCurrentRulesDetailed(), { 628 | '@scope-with-dash/foo-rule': [2], 629 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule': [2], 630 | '@scope/foo-rule': [2], 631 | '@scope/scoped-plugin/foo-rule': [2], 632 | 'foo-rule': [2], 633 | 'bar-rule': [2] 634 | }); 635 | }); 636 | 637 | it('specifiedFile (absolute path) - plugin rules', async () => { 638 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute); 639 | assert.deepEqual(ruleFinder.getPluginRules(), [ 640 | '@scope-with-dash/bar-rule', 641 | '@scope-with-dash/foo-rule', 642 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 643 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 644 | '@scope/bar-rule', 645 | '@scope/foo-rule', 646 | '@scope/scoped-plugin/bar-rule', 647 | '@scope/scoped-plugin/foo-rule', 648 | 'plugin/bar-rule', 649 | 'plugin/baz-rule', 650 | 'plugin/foo-rule' 651 | ]); 652 | }); 653 | 654 | it('specifiedFile (absolute path) - plugin rules including deprecated', async () => { 655 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute, {includeDeprecated: true}); 656 | assert.deepEqual(ruleFinder.getPluginRules(), [ 657 | '@scope-with-dash/bar-rule', 658 | '@scope-with-dash/foo-rule', 659 | '@scope-with-dash/old-plugin-rule', 660 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 661 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 662 | '@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule', 663 | '@scope/bar-rule', 664 | '@scope/foo-rule', 665 | '@scope/old-plugin-rule', 666 | '@scope/scoped-plugin/bar-rule', 667 | '@scope/scoped-plugin/foo-rule', 668 | '@scope/scoped-plugin/old-plugin-rule', 669 | 'plugin/bar-rule', 670 | 'plugin/baz-rule', 671 | 'plugin/foo-rule', 672 | 'plugin/old-plugin-rule' 673 | ]); 674 | }); 675 | 676 | it('specifiedFile (absolute path) - all available rules', async () => { 677 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute); 678 | assert.deepEqual( 679 | ruleFinder.getAllAvailableRules(), 680 | [ 681 | '@scope-with-dash/bar-rule', 682 | '@scope-with-dash/foo-rule', 683 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 684 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 685 | '@scope/bar-rule', 686 | '@scope/foo-rule', 687 | '@scope/scoped-plugin/bar-rule', 688 | '@scope/scoped-plugin/foo-rule', 689 | 'bar-rule', 690 | 'baz-rule', 691 | 'foo-rule', 692 | 'plugin/bar-rule', 693 | 'plugin/baz-rule', 694 | 'plugin/foo-rule' 695 | ] 696 | ); 697 | }); 698 | 699 | it('specifiedFile (absolute path) - all available rules including deprecated', async () => { 700 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute, {includeDeprecated: true}); 701 | assert.deepEqual( 702 | ruleFinder.getAllAvailableRules(), 703 | [ 704 | '@scope-with-dash/bar-rule', 705 | '@scope-with-dash/foo-rule', 706 | '@scope-with-dash/old-plugin-rule', 707 | '@scope-with-dash/scoped-with-dash-plugin/bar-rule', 708 | '@scope-with-dash/scoped-with-dash-plugin/foo-rule', 709 | '@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule', 710 | '@scope/bar-rule', 711 | '@scope/foo-rule', 712 | '@scope/old-plugin-rule', 713 | '@scope/scoped-plugin/bar-rule', 714 | '@scope/scoped-plugin/foo-rule', 715 | '@scope/scoped-plugin/old-plugin-rule', 716 | 'bar-rule', 717 | 'baz-rule', 718 | 'foo-rule', 719 | 'old-rule', 720 | 'plugin/bar-rule', 721 | 'plugin/baz-rule', 722 | 'plugin/foo-rule', 723 | 'plugin/old-plugin-rule' 724 | ] 725 | ); 726 | }); 727 | 728 | it('specifiedFile (absolute path) without rules - plugin rules', async () => { 729 | const ruleFinder = await getRuleFinder(noRulesFile); 730 | assert.deepEqual(ruleFinder.getPluginRules(), [ 731 | 'plugin/bar-rule', 732 | 'plugin/baz-rule', 733 | 'plugin/foo-rule' 734 | ]); 735 | }); 736 | 737 | it('dedupes plugin rules - all available rules', async () => { 738 | const ruleFinder = await getRuleFinderForDedupeTests(noDuplicateRulesFiles); 739 | assert.deepEqual(ruleFinder.getAllAvailableRules(), [ 740 | 'bar-rule', 741 | 'foo-rule', 742 | 'plugin/duplicate-bar-rule', 743 | 'plugin/duplicate-foo-rule' 744 | ]); 745 | }); 746 | 747 | it('dedupes plugin rules - unused rules', async () => { 748 | const ruleFinder = await getRuleFinderForDedupeTests(noDuplicateRulesFiles); 749 | assert.deepEqual(ruleFinder.getUnusedRules(), [ 750 | 'bar-rule', 751 | 'plugin/duplicate-foo-rule' 752 | ]); 753 | }); 754 | 755 | it('specifiedFile (absolute path) without deprecated rules - deprecated rules', async () => { 756 | const ruleFinder = await getRuleFinder(specifiedFileAbsolute); 757 | assert.deepEqual(ruleFinder.getDeprecatedRules(), []); 758 | }); 759 | 760 | it('specifiedFile (absolute path) with deprecated rules - deprecated rules', async () => { 761 | const ruleFinder = await getRuleFinder(usingDeprecatedRulesFile); 762 | assert.deepEqual(ruleFinder.getDeprecatedRules(), [ 763 | '@scope-with-dash/old-plugin-rule', 764 | '@scope-with-dash/scoped-with-dash-plugin/old-plugin-rule', 765 | '@scope/old-plugin-rule', 766 | '@scope/scoped-plugin/old-plugin-rule', 767 | 'old-rule', 768 | 'plugin/old-plugin-rule' 769 | ]); 770 | }); 771 | 772 | it('check overrides - unused rules', async () => { 773 | const ruleFinder = await getRuleFinder(usingWithOverridesFile, {'ext': ['.txt', '.json']}); 774 | assert.deepEqual(ruleFinder.getUnusedRules(), [ 775 | "@scope-with-dash/bar-rule", 776 | "@scope-with-dash/foo-rule", 777 | "@scope-with-dash/scoped-with-dash-plugin/bar-rule", 778 | "@scope-with-dash/scoped-with-dash-plugin/foo-rule", 779 | "@scope/bar-rule", 780 | "@scope/scoped-plugin/bar-rule", 781 | "bar-rule", 782 | "baz-rule", 783 | "plugin/bar-rule", 784 | "plugin/baz-rule", 785 | "plugin/foo-rule", 786 | ]); 787 | }); 788 | 789 | (isV8Eslint ? it : it.skip)('flat config - should throw an exception if FlatESLint is not defined', async () => { 790 | try { 791 | await getRuleFinderNoFlatSupport(specifiedFlatConfigFileRelative, {useFlatConfig: true}) 792 | assert.fail('Expected an error to be thrown'); 793 | } catch (error) { 794 | assert.strictEqual(error, 'This version of ESLint does not support flat config.') 795 | } 796 | }); 797 | 798 | (isV8Eslint ? describe : describe.skip)('flat config - supported', () => { 799 | it('specifiedFile (relative path) - unused rules', async () => { 800 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, {useFlatConfig: true}); 801 | assert.deepEqual(ruleFinder.getUnusedRules(), [ 802 | 'bar-rule', 803 | 'baz-rule', 804 | 'plugin/bar-rule' 805 | ]); 806 | }); 807 | 808 | it('specifiedFile (relative path) - unused rules including deprecated', async () => { 809 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, {includeDeprecated: true, useFlatConfig: true}); 810 | assert.deepEqual(ruleFinder.getUnusedRules(), [ 811 | 'bar-rule', 812 | 'baz-rule', 813 | 'old-rule', 814 | 'plugin/bar-rule', 815 | 'plugin/old-plugin-rule' 816 | ]); 817 | }); 818 | 819 | it('specifiedFile (relative path) - current rules', async () => { 820 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, {useFlatConfig: true}); 821 | assert.deepEqual(ruleFinder.getCurrentRules(), [ 822 | 'foo-rule', 823 | 'plugin/foo-rule' 824 | ]); 825 | }); 826 | 827 | it('specifiedFile (relative path) - current rules with ext', async () => { 828 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, { ext: ['.json'], useFlatConfig: true}); 829 | assert.deepEqual(ruleFinder.getCurrentRules(), [ 830 | 'jsonPlugin/foo-rule' 831 | ]); 832 | }); 833 | 834 | it('specifiedFile (relative path) - current rules with ext without dot', async () => { 835 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, { ext: ['json'], useFlatConfig: true}); 836 | assert.deepEqual(ruleFinder.getCurrentRules(), [ 837 | 'jsonPlugin/foo-rule' 838 | ]); 839 | }); 840 | 841 | it('specifiedFile (relative path) - current rules with ext not found', async () => { 842 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, { ext: ['.ts'], useFlatConfig: true }); 843 | assert.deepEqual(ruleFinder.getCurrentRules(), []); 844 | }); 845 | 846 | it('specifiedFile (relative path) - plugin rules', async () => { 847 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, { useFlatConfig: true}); 848 | assert.deepEqual(ruleFinder.getPluginRules(), [ 849 | 'plugin/bar-rule', 850 | 'plugin/foo-rule' 851 | ]); 852 | }); 853 | 854 | it('specifiedFile (relative path) - plugin rules including deprecated', async () => { 855 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, {includeDeprecated: true, useFlatConfig: true}); 856 | assert.deepEqual(ruleFinder.getPluginRules(), [ 857 | 'plugin/bar-rule', 858 | 'plugin/foo-rule', 859 | 'plugin/old-plugin-rule' 860 | ]); 861 | }); 862 | 863 | it('specifiedFile (relative path) - all available rules', async () => { 864 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, { useFlatConfig: true }); 865 | assert.deepEqual( 866 | ruleFinder.getAllAvailableRules(), 867 | [ 868 | 'bar-rule', 869 | 'baz-rule', 870 | 'foo-rule', 871 | 'plugin/bar-rule', 872 | 'plugin/foo-rule' 873 | ] 874 | ); 875 | }); 876 | 877 | it('specifiedFile (relative path) - all available rules without core', async () => { 878 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, {omitCore: true, useFlatConfig: true}); 879 | assert.deepEqual( 880 | ruleFinder.getAllAvailableRules(), 881 | [ 882 | 'plugin/bar-rule', 883 | 'plugin/foo-rule' 884 | ] 885 | ); 886 | }); 887 | 888 | it('specifiedFile (relative path) - all available rules including deprecated', async () => { 889 | const ruleFinder = await getRuleFinder(specifiedFlatConfigFileRelative, {includeDeprecated: true, useFlatConfig: true}); 890 | assert.deepEqual( 891 | ruleFinder.getAllAvailableRules(), 892 | [ 893 | 'bar-rule', 894 | 'baz-rule', 895 | 'foo-rule', 896 | 'old-rule', 897 | 'plugin/bar-rule', 898 | 'plugin/foo-rule', 899 | 'plugin/old-plugin-rule' 900 | ] 901 | ); 902 | }); 903 | }); 904 | }); 905 | -------------------------------------------------------------------------------- /test/lib/sort-rules.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const sortRules = require('../../src/lib/sort-rules'); 3 | 4 | describe('sort-rules', () => { 5 | it('should return sorted rules', () => { 6 | assert.deepEqual( 7 | sortRules(['a', 'b', 'c']), 8 | ['a', 'b', 'c'] 9 | ); 10 | assert.deepEqual( 11 | sortRules(['c', 'b', 'a']), 12 | ['a', 'b', 'c'] 13 | ); 14 | assert.deepEqual( 15 | sortRules(['aa', 'a', 'ab', 'b', 'c']), 16 | ['a', 'aa', 'ab', 'b', 'c'] 17 | ); 18 | }); 19 | 20 | it('should return sorted rule configs', () => { 21 | assert.deepEqual( 22 | sortRules({'bar-rule': {config1: '1', config2: '2'}, 'baz-rule': {config1: '3', config2: '4'}}), 23 | [{'bar-rule': {config1: '1', config2: '2'}}, {'baz-rule': {config1: '3', config2: '4'}}] 24 | ); 25 | assert.deepEqual( 26 | sortRules({'foo-rule': {config1: '1', config2: '2'}, 'bar-rule': {config1: '3', config2: '4'}}), 27 | [{'bar-rule': {config1: '3', config2: '4'}}, {'foo-rule': {config1: '1', config2: '2'}}] 28 | ); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/lib/stringify-rule-config.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const stringifyRuleConfig = require('../../src/lib/stringify-rule-config'); 3 | 4 | describe('stringify rule config', () => { 5 | it('should return a string', () => { 6 | assert.equal( 7 | stringifyRuleConfig('A simple string'), 8 | 'A simple string' 9 | ); 10 | }); 11 | 12 | it('should return \'-\' for "undefined"', () => { 13 | assert.equal( 14 | stringifyRuleConfig(undefined), 15 | '-' 16 | ); 17 | }); 18 | 19 | it('should return a JSON.stringify\'ed result for any object', () => { 20 | assert.deepEqual( 21 | stringifyRuleConfig([2, 'foo', {bar: true}]), 22 | JSON.stringify([2, 'foo', {bar: true}]) 23 | ); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers js:babel-core/register 2 | --------------------------------------------------------------------------------