├── .eslintignore ├── .eslintrc-multiple-rulesdir.js ├── .eslintrc-single-rulesdir.js ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── fake-rule-dir-one └── fake-rule.js ├── fake-rule-dir-three └── yet-another-fake-rule.ts ├── fake-rule-dir-two └── another-fake-rule.js ├── index.js └── package.json /.eslintignore: -------------------------------------------------------------------------------- 1 | !.eslintrc.js 2 | -------------------------------------------------------------------------------- /.eslintrc-multiple-rulesdir.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const PACKAGE_NAME = require('./package').name; 6 | 7 | const SYMLINK_LOCATION = path.join(__dirname, 'node_modules', PACKAGE_NAME); 8 | 9 | // Symlink node_modules/{package name} to this directory 10 | // so that ESLint resolves this plugin name correctly. 11 | // (Yes, this plugin still has to hack node_modules to bootstrap itself.) 12 | if (!fs.existsSync(SYMLINK_LOCATION)) { 13 | fs.symlinkSync(__dirname, SYMLINK_LOCATION); 14 | } 15 | 16 | require('.').RULES_DIR = [path.resolve('fake-rule-dir-one'), path.resolve('fake-rule-dir-two'), path.resolve('fake-rule-dir-three')]; 17 | 18 | module.exports = { 19 | extends: 'airbnb-base', 20 | parserOptions: { 21 | sourceType: 'script', 22 | }, 23 | rules: { 24 | 'global-require': 'off', 25 | 'import/no-dynamic-require': 'off', 26 | 'rulesdir/fake-rule': 'error', 27 | 'rulesdir/another-fake-rule': 'error', 28 | 'rulesdir/yet-another-fake-rule': 'error', 29 | }, 30 | plugins: [PACKAGE_NAME], 31 | }; 32 | -------------------------------------------------------------------------------- /.eslintrc-single-rulesdir.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const PACKAGE_NAME = require('./package').name; 6 | 7 | const SYMLINK_LOCATION = path.join(__dirname, 'node_modules', PACKAGE_NAME); 8 | 9 | // Symlink node_modules/{package name} to this directory 10 | // so that ESLint resolves this plugin name correctly. 11 | // (Yes, this plugin still has to hack node_modules to bootstrap itself.) 12 | if (!fs.existsSync(SYMLINK_LOCATION)) { 13 | fs.symlinkSync(__dirname, SYMLINK_LOCATION); 14 | } 15 | 16 | require('.').RULES_DIR = path.resolve('fake-rule-dir-one'); 17 | 18 | module.exports = { 19 | extends: 'airbnb-base', 20 | parserOptions: { 21 | sourceType: 'script', 22 | }, 23 | rules: { 24 | 'global-require': 'off', 25 | 'import/no-dynamic-require': 'off', 26 | 'rulesdir/fake-rule': 'error', 27 | }, 28 | plugins: [PACKAGE_NAME], 29 | }; 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.2.3 (2024-10-11) 4 | 5 | - `peerDependencies` are now correctly set 6 | 7 | ## v0.2.2 (2022-12-17) 8 | 9 | - Support loading `.ts` rules from a directory 10 | 11 | ## Previous versions 12 | 13 | See the [git history](https://github.com/eslint-community/eslint-plugin-rulesdir). 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © 2017 Teddy Katz 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-rulesdir 2 | 3 | Allows a local directory containing ESLint rules directory to be easily used. This is a substitute for the `--rulesdir` option that can be used without a command-line flag. 4 | 5 | **Experimental:** This plugin is currently a proof-of-concept. Its API is likely to change in the future. 6 | 7 | ## Installation 8 | 9 | You'll first need to install [ESLint](http://eslint.org): 10 | 11 | ``` 12 | $ npm i eslint --save-dev 13 | ``` 14 | 15 | Next, install `eslint-plugin-rulesdir`: 16 | 17 | ``` 18 | $ npm install eslint-plugin-rulesdir --save-dev 19 | ``` 20 | 21 | **Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-rulesdir` globally. 22 | 23 | ## Usage 24 | 25 | To use this plugin, you must load it manually first and set its `RULES_DIR` property to a path, or an array of paths. The paths are resolved from the current working directory, and indicates where you would like the plugin to load your rules from. This is easiest if you use a JavaScript config file (`.eslintrc.js`), and use a local installation of ESLint. 26 | 27 | ```js 28 | // .eslintrc.js 29 | const rulesDirPlugin = require('eslint-plugin-rulesdir'); 30 | rulesDirPlugin.RULES_DIR = 'tools/eslint-rules'; // (an example folder where your rules might be stored) 31 | 32 | // You can also provide an array if you have multiple folders with rules in: 33 | rulesDirPlugin.RULES_DIR = ['tools/eslint-rules', 'tools/other-eslint-rules']; 34 | ``` 35 | 36 | Then you should add `rulesdir` to the plugins section of your `.eslintrc.js` file. 37 | 38 | ```js 39 | { 40 | plugins: [ 41 | 'rulesdir' 42 | ] 43 | } 44 | ``` 45 | 46 | Finally, you can configure your local rules, prefixed with `rulesdir/`. 47 | 48 | ```js 49 | { 50 | rules: { 51 | // 52 | 'rulesdir/my-internal-foo-rule': 'error', 53 | 'rulesdir/my-internal-bar-rule': ['warn', 2] 54 | } 55 | } 56 | ``` 57 | 58 | All of the rules from your configured rules directory will be available. In this example, we assumed there were rule files in `tools/eslint-rules/my-internal-foo-rule.js` and `tools/eslint-rules/my-internal-bar-rule.js`. 59 | 60 | ## Prior Art 61 | 62 | * [`eslint-plugin-local-rules`](https://github.com/cletusw/eslint-plugin-local-rules) 63 | 64 | ## License 65 | 66 | [MIT](https://github.com/eslint-community/eslint-plugin-rulesdir/blob/main/LICENSE.md) 67 | -------------------------------------------------------------------------------- /fake-rule-dir-one/fake-rule.js: -------------------------------------------------------------------------------- 1 | // This rule doesn't do anything. 2 | // it only exists to make sure a rule is found and the plugin is working. 3 | 4 | 'use strict'; 5 | 6 | module.exports = { create: () => ({}) }; 7 | -------------------------------------------------------------------------------- /fake-rule-dir-three/yet-another-fake-rule.ts: -------------------------------------------------------------------------------- 1 | // This rule doesn't do anything. 2 | // it only exists to make sure a rule is found and the plugin is working. 3 | 4 | 'use strict'; 5 | 6 | module.exports = { create: () => ({}) }; 7 | -------------------------------------------------------------------------------- /fake-rule-dir-two/another-fake-rule.js: -------------------------------------------------------------------------------- 1 | // This rule doesn't do anything. 2 | // it only exists to make sure a rule is found and the plugin is working. 3 | 4 | 'use strict'; 5 | 6 | module.exports = { create: () => ({}) }; 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Allows a local ESLint rules directory to be used without a command-line flag 3 | * @author Teddy Katz 4 | */ 5 | 6 | 'use strict'; 7 | 8 | //------------------------------------------------------------------------------ 9 | // Requirements 10 | //------------------------------------------------------------------------------ 11 | 12 | const fs = require('fs'); 13 | const path = require('path'); 14 | 15 | //------------------------------------------------------------------------------ 16 | // Plugin Definition 17 | //------------------------------------------------------------------------------ 18 | 19 | const cache = {}; 20 | 21 | const ruleExtensions = new Set(['.js', '.cjs', '.mjs', '.ts', '.cts', '.mts']); 22 | 23 | module.exports = { 24 | get rules() { 25 | const RULES_DIR = module.exports.RULES_DIR; 26 | if (typeof module.exports.RULES_DIR !== 'string' && !Array.isArray(RULES_DIR)) { 27 | throw new Error('To use eslint-plugin-rulesdir, you must load it beforehand and set the `RULES_DIR` property on the module to a string or an array of strings.'); 28 | } 29 | const cacheKey = JSON.stringify(RULES_DIR); 30 | if (!cache[cacheKey]) { 31 | const rules = Array.isArray(RULES_DIR) ? RULES_DIR : [RULES_DIR]; 32 | const rulesObject = {}; 33 | rules.forEach((rulesDir) => { 34 | fs.readdirSync(rulesDir) 35 | .filter(filename => ruleExtensions.has(path.extname(filename))) 36 | .map(filename => path.resolve(rulesDir, filename)) 37 | .forEach((absolutePath) => { 38 | const ruleName = path.basename(absolutePath, path.extname(absolutePath)); 39 | if (rulesObject[ruleName]) { 40 | throw new Error(`eslint-plugin-rulesdir found two rules with the same name: ${ruleName}`); 41 | } 42 | rulesObject[ruleName] = require(absolutePath); 43 | }); 44 | }); 45 | cache[cacheKey] = rulesObject; 46 | } 47 | return cache[cacheKey]; 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-rulesdir", 3 | "version": "0.2.3", 4 | "description": "Allows a local ESLint rules directory to be used without a command-line flag", 5 | "license": "MIT", 6 | "keywords": [ 7 | "eslint", 8 | "eslintplugin", 9 | "eslint-plugin" 10 | ], 11 | "author": "Teddy Katz", 12 | "scripts": { 13 | "test": "npm run lint-single-rulesdir && npm run lint-multiple-rulesdir", 14 | "lint-single-rulesdir": "eslint --config .eslintrc-single-rulesdir.js *.js **/*.js", 15 | "lint-multiple-rulesdir": "eslint --config .eslintrc-multiple-rulesdir.js *.js **/*.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/eslint-community/eslint-plugin-rulesdir" 20 | }, 21 | "funding": "https://opencollective.com/eslint", 22 | "devDependencies": { 23 | "eslint": ">=3.0.0", 24 | "eslint-config-airbnb-base": "^11.2.0", 25 | "eslint-plugin-import": "^2.6.1" 26 | }, 27 | "peerDependencies": { 28 | "eslint": ">=3.0.0" 29 | }, 30 | "engines": { 31 | "node": ">=4.0.0" 32 | } 33 | } 34 | --------------------------------------------------------------------------------