├── .npmrc ├── .gitattributes ├── .eslintignore ├── .gitignore ├── .travis.yml ├── .nycrc ├── scripts ├── update.js ├── lib │ ├── plugin-id.js │ ├── update-lib-index.js │ ├── update-lib-configs-recommended.js │ ├── update-readme.js │ ├── rules.js │ └── update-docs-headers.js └── add-rule.js ├── lib ├── configs │ └── recommended.js ├── index.js └── rules │ └── example-rule.js ├── tests └── lib │ └── rules │ └── example-rule.js ├── docs └── rules │ └── example-rule.md ├── LICENSE ├── .eslintrc.js ├── package.json └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock = false 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | !.*.js 2 | /.nyc_output 3 | /coverage 4 | /node_modules 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.nyc_output 2 | /coverage 3 | /node_modules 4 | /test.* 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8.10.0" 4 | - "10" 5 | - "12" 6 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "lib/**/*.js" 4 | ], 5 | "reporter": [ 6 | "lcov", 7 | "text-summary" 8 | ], 9 | "sourceMap": true 10 | } 11 | -------------------------------------------------------------------------------- /scripts/update.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("./lib/update-docs-headers"); 4 | require("./lib/update-lib-configs-recommended"); 5 | require("./lib/update-lib-index"); 6 | require("./lib/update-readme"); 7 | -------------------------------------------------------------------------------- /lib/configs/recommended.js: -------------------------------------------------------------------------------- 1 | /* DON'T EDIT THIS FILE. This is generated by 'scripts/lib/update-lib-configs-recommended.js' */ 2 | "use strict"; 3 | 4 | module.exports = { 5 | plugins: ["xxxx"], 6 | rules: { 7 | "xxxx/example-rule": "error" 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* DON'T EDIT THIS FILE. This is generated by 'scripts/lib/update-lib-index.js' */ 2 | "use strict"; 3 | 4 | module.exports = { 5 | configs: { 6 | recommended: require("./configs/recommended") 7 | }, 8 | rules: { 9 | "example-rule": require("./rules/example-rule") 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /tests/lib/rules/example-rule.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { RuleTester } = require("eslint"); 4 | const rule = require("../../../lib/rules/example-rule"); 5 | 6 | new RuleTester().run("example-rule", rule, { 7 | valid: [ 8 | "var foo = 1;" 9 | ], 10 | invalid: [ 11 | { 12 | code: "var example = 1;", 13 | errors: ["'example' identifier is forbidden."] 14 | } 15 | ] 16 | }); 17 | -------------------------------------------------------------------------------- /scripts/lib/plugin-id.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { name } = require("../../package.json"); 4 | 5 | let pluginId = name; 6 | 7 | if (name.startsWith("eslint-plugin-")) { 8 | pluginId = name.slice("eslint-plugin-".length); 9 | } else { 10 | const match = /^(@.+)\/eslint-plugin(?:-(.+))?$/; 11 | 12 | if (match) { 13 | if (match[2]) { 14 | pluginId = `${match[1]}/${match[2]}`; 15 | } else { 16 | pluginId = match[1]; 17 | } 18 | } 19 | } 20 | 21 | module.exports = { pluginId }; 22 | -------------------------------------------------------------------------------- /docs/rules/example-rule.md: -------------------------------------------------------------------------------- 1 | # xxxx/example-rule 2 | > An example rule. 3 | > - ⭐️ This rule is included in `plugin:xxxx/recommended` preset. 4 | 5 | This is an example. 6 | 7 | ## Rule Details 8 | 9 | This rule aimed at disallowing `example` identifiers. 10 | 11 | Examples of **incorrect** code: 12 | 13 | ```js 14 | /*eslint template/example-rule: error */ 15 | 16 | var example = 1; 17 | ``` 18 | 19 | Examples of **correct** code: 20 | 21 | ```js 22 | /*eslint template/example-rule: error */ 23 | 24 | var foo = 1; 25 | ``` 26 | 27 | ## Options 28 | 29 | Nothing. 30 | 31 | ## Implementation 32 | 33 | - [Rule source](../../lib/rules/example-rule.js) 34 | - [Test source](../../tests/lib/rules/example-rule.js) 35 | -------------------------------------------------------------------------------- /lib/rules/example-rule.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | meta: { 5 | docs: { 6 | description: "An example rule.", 7 | category: "Stylistic Issues", 8 | recommended: true, 9 | url: "https://github.com/mysticatea/eslint-plugin-template/blob/master/docs/rules/example-rule.md" 10 | }, 11 | fixable: null, 12 | messages: { 13 | disallowExample: "'example' identifier is forbidden." 14 | }, 15 | schema: [], 16 | type: "suggestion" 17 | }, 18 | 19 | create(context) { 20 | return { 21 | "Identifier[name='example']"(node) { 22 | context.report({ 23 | node, 24 | messageId: "disallowExample" 25 | }); 26 | } 27 | }; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /scripts/lib/update-lib-index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const { CLIEngine } = require("eslint"); 6 | const { rules } = require("./rules"); 7 | 8 | const filePath = path.resolve(__dirname, "../../lib/index.js"); 9 | const rawContent = `/* DON'T EDIT THIS FILE. This is generated by 'scripts/lib/update-lib-index.js' */ 10 | "use strict" 11 | 12 | module.exports = { 13 | configs: { 14 | recommended: require("./configs/recommended") 15 | }, 16 | rules: { 17 | ${rules.map(rule => `"${rule.name}": require("./rules/${rule.name}")`).join(",\n ")} 18 | } 19 | }; 20 | `; 21 | const engine = new CLIEngine({ fix: true }); 22 | const lintResult = engine.executeOnText(rawContent, filePath); 23 | const content = lintResult.results[0].output || rawContent; 24 | 25 | fs.writeFileSync(filePath, content); 26 | -------------------------------------------------------------------------------- /scripts/lib/update-lib-configs-recommended.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const { CLIEngine } = require("eslint"); 6 | const { pluginId } = require("./plugin-id"); 7 | const { rules } = require("./rules"); 8 | 9 | const filePath = path.resolve(__dirname, "../../lib/configs/recommended.js"); 10 | const rawContent = `/* DON'T EDIT THIS FILE. This is generated by 'scripts/lib/update-lib-configs-recommended.js' */ 11 | "use strict" 12 | 13 | module.exports = { 14 | plugins: ["${pluginId}"], 15 | rules: { 16 | ${rules 17 | .filter(rule => rule.recommended) 18 | .map(rule => `"${rule.id}": "error"`) 19 | .join(",\n ")} 20 | } 21 | }; 22 | `; 23 | const engine = new CLIEngine({ fix: true }); 24 | const lintResult = engine.executeOnText(rawContent, filePath); 25 | const content = lintResult.results[0].output || rawContent; 26 | 27 | fs.writeFileSync(filePath, content); 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | root: true, 5 | plugins: [ 6 | "eslint-plugin" 7 | ], 8 | extends: [ 9 | "eslint", 10 | "plugin:eslint-plugin/recommended" 11 | ], 12 | rules: { 13 | "eslint-plugin/consistent-output": "error", 14 | "eslint-plugin/no-deprecated-context-methods": "error", 15 | "eslint-plugin/prefer-output-null": "error", 16 | "eslint-plugin/prefer-placeholders": "error", 17 | "eslint-plugin/report-message-format": ["error", "[^a-z].*\\.$"], 18 | "eslint-plugin/require-meta-type": "error", 19 | "eslint-plugin/test-case-property-ordering": ["error", [ 20 | "filename", 21 | "code", 22 | "output", 23 | "options", 24 | "parser", 25 | "parserOptions", 26 | "globals", 27 | "env", 28 | "errors" 29 | ]], 30 | "eslint-plugin/test-case-shorthand-strings": "error" 31 | }, 32 | overrides: [ 33 | { 34 | files: "scripts/**/*.js", 35 | rules: { 36 | "no-console": "off" 37 | } 38 | } 39 | ] 40 | }; 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-xxxx", 3 | "version": "1.0.0", 4 | "description": "", 5 | "engines": { 6 | "node": ">=8.10.0" 7 | }, 8 | "main": "lib/index.js", 9 | "files": [ 10 | "lib" 11 | ], 12 | "scripts": { 13 | "add-rule": "node scripts/add-rule", 14 | "lint": "eslint lib scripts tests", 15 | "update": "node scripts/update", 16 | "pretest": "npm run -s lint", 17 | "test": "nyc mocha \"tests/lib/**/*.js\" --reporter progress", 18 | "preversion": "npm test", 19 | "version": "npm run -s update", 20 | "postversion": "git push && git push --tags" 21 | }, 22 | "peerDependencies": { 23 | "eslint": ">=5.16.0" 24 | }, 25 | "dependencies": {}, 26 | "devDependencies": { 27 | "@types/node": "^12.0.6", 28 | "eslint": "^5.16.0", 29 | "eslint-config-eslint": "^5.0.1", 30 | "eslint-plugin-eslint-plugin": "^2.1.0", 31 | "eslint-plugin-node": "^9.1.0", 32 | "mocha": "^6.1.4", 33 | "nyc": "^14.1.1" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/???/eslint-plugin-xxxx.git" 38 | }, 39 | "keywords": [ 40 | "eslint", 41 | "eslintplugin", 42 | "eslint-plugin" 43 | ], 44 | "author": "", 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/???/eslint-plugin-xxxx/issues" 48 | }, 49 | "homepage": "https://github.com/???/eslint-plugin-xxxx#readme" 50 | } 51 | -------------------------------------------------------------------------------- /scripts/lib/update-readme.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const { categories } = require("./rules"); 6 | 7 | /** 8 | * Render a given rule as a table row. 9 | * @param {RuleInfo} rule The rule information. 10 | * @returns {string} The table row. 11 | */ 12 | function renderRule(rule) { 13 | const mark = `${rule.recommended ? "⭐️" : ""}${rule.fixable ? "✒️" : ""}`; 14 | const link = `[${rule.id}](./docs/rules/${rule.name}.md)`; 15 | const description = rule.description || "(no description)"; 16 | 17 | return `| ${link} | ${description} | ${mark} |`; 18 | } 19 | 20 | /** 21 | * Render a given category as a section. 22 | * @param {CategoryInfo} category The rule information. 23 | * @returns {string} The section. 24 | */ 25 | function renderCategory(category) { 26 | if (category.rules.length === 0) { 27 | return ""; 28 | } 29 | return `### ${category.id} 30 | 31 | | Rule ID | Description | | 32 | |:--------|:------------|:--:| 33 | ${category.rules.map(renderRule).join("\n")} 34 | `; 35 | } 36 | 37 | const filePath = path.resolve(__dirname, "../../README.md"); 38 | const content = categories.map(renderCategory).filter(Boolean).join("\n"); 39 | 40 | fs.writeFileSync( 41 | filePath, 42 | fs 43 | .readFileSync(filePath, "utf8") 44 | .replace( 45 | /[\s\S]*/u, 46 | `\n${content}\n` 47 | ) 48 | ); 49 | -------------------------------------------------------------------------------- /scripts/lib/rules.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const rootDir = path.resolve(__dirname, "../../lib/rules/"); 6 | const { pluginId } = require("./plugin-id"); 7 | 8 | /** 9 | * @typedef {Object} RuleInfo 10 | * @property {string} filePath The path to the rule definition. 11 | * @property {string} id The rule ID. (This includes prefix.) 12 | * @property {string} name The rule name. (This doesn't include prefix.) 13 | * @property {string} category The category ID. 14 | * @property {string} description The description of this rule. 15 | * @property {boolean} recommended The flag to indicate a recommended rule. 16 | * @property {boolean} deprecated The flag to indicate a deprecated rule. 17 | * @property {boolean} fixable The flag to indicate a fixable rule. 18 | * @property {string[]} replacedBy The flag to indicate a fixable rule. 19 | */ 20 | 21 | /** 22 | * @typedef {Object} CategoryInfo 23 | * @property {string} id The category ID. 24 | * @property {RuleInfo[]} rules The rules which belong to this category. 25 | */ 26 | 27 | /** @type {RuleInfo[]} */ 28 | const rules = fs.readdirSync(rootDir) 29 | .sort() 30 | .map(filename => { 31 | const filePath = path.join(rootDir, filename); 32 | const name = filename.slice(0, -3); 33 | const { meta } = require(filePath); 34 | 35 | return { 36 | filePath, 37 | id: `${pluginId}/${name}`, 38 | name, 39 | deprecated: Boolean(meta.deprecated), 40 | fixable: Boolean(meta.fixable), 41 | replacedBy: [], 42 | ...meta.docs 43 | }; 44 | }); 45 | 46 | /** @type {CategoryInfo[]} */ 47 | const categories = [ 48 | "Possible Errors", 49 | "Best Practices", 50 | "Stylistic Issues" 51 | ].map(id => ({ 52 | id, 53 | rules: rules.filter(rule => rule.category === id && !rule.deprecated) 54 | })); 55 | 56 | module.exports = { rules, categories }; 57 | -------------------------------------------------------------------------------- /scripts/add-rule.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const { pluginId } = require("./lib/plugin-id"); 6 | const ruleId = process.argv[2]; 7 | 8 | // Require rule ID. 9 | if (!ruleId) { 10 | console.error("Usage: npm run add-rule "); 11 | process.exitCode = 1; 12 | return; 13 | } 14 | 15 | const docPath = path.resolve(__dirname, "../docs/rules", `${ruleId}.md`); 16 | const rulePath = path.resolve(__dirname, "../lib/rules", `${ruleId}.js`); 17 | const testPath = path.resolve(__dirname, "../tests/lib/rules", `${ruleId}.js`); 18 | 19 | // Overwrite check. 20 | for (const filePath of [docPath, rulePath, testPath]) { 21 | if (fs.existsSync(filePath)) { 22 | console.error("%o has existed already.", path.relative(process.cwd(), filePath)); 23 | process.exitCode = 1; 24 | return; 25 | } 26 | } 27 | 28 | // Generate files. 29 | fs.writeFileSync(docPath, `# ${pluginId}/${ruleId} 30 | > (TODO: summary) 31 | 32 | (TODO: why is this rule useful?) 33 | 34 | ## Rule Details 35 | 36 | (TODO: how does this rule check code?) 37 | 38 | ## Options 39 | 40 | (TODO: what do options exist?) 41 | `); 42 | 43 | fs.writeFileSync(rulePath, `"use strict"; 44 | 45 | module.exports = { 46 | meta: { 47 | docs: { 48 | // TODO: write the rule summary. 49 | description: "", 50 | 51 | // TODO: choose the rule category. 52 | category: "Possible Errors", 53 | category: "Best Practices", 54 | category: "Stylistic Issues", 55 | 56 | recommended: false, 57 | url: "" 58 | }, 59 | 60 | fixable: null, 61 | messages: {}, 62 | schema: [], 63 | 64 | // TODO: choose the rule type. 65 | type: "problem", 66 | type: "suggestion", 67 | type: "layout" 68 | }, 69 | 70 | create(context) { 71 | const sourceCode = context.getSourceCode(); 72 | return {} 73 | } 74 | }; 75 | `); 76 | 77 | fs.writeFileSync(testPath, `"use strict"; 78 | 79 | const { RuleTester } = require("eslint"); 80 | const rule = require("../../../lib/rules/${ruleId}"); 81 | 82 | new RuleTester().run("${ruleId}", rule, { 83 | valid: [], 84 | invalid: [] 85 | }); 86 | `); 87 | -------------------------------------------------------------------------------- /scripts/lib/update-docs-headers.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const { pluginId } = require("./plugin-id"); 6 | const { rules } = require("./rules"); 7 | const headerPattern = /^#.+\n(?:>.+\n)*\n+/u; 8 | const footerPattern = /\n+## Implementation[\s\S]*$/u; 9 | const ruleRoot = path.resolve(__dirname, "../../lib/rules"); 10 | const testRoot = path.resolve(__dirname, "../../tests/lib/rules"); 11 | const docsRoot = path.resolve(__dirname, "../../docs/rules"); 12 | const listFormatter = new Intl.ListFormat("en", { type: "conjunction" }); 13 | 14 | /** @typedef {import("./rules").RuleInfo} RuleInfo */ 15 | 16 | /** 17 | * Render the document header of a given rule. 18 | * @param {RuleInfo} rule The rule information. 19 | * @returns {string} The document header. 20 | */ 21 | function renderHeader(rule) { 22 | const lines = [`# ${rule.id}`, `> ${rule.description}`]; 23 | 24 | if (rule.recommended) { 25 | lines.push( 26 | `> - ⭐️ This rule is included in \`plugin:${pluginId}/recommended\` preset.` 27 | ); 28 | } 29 | if (rule.fixable) { 30 | lines.push( 31 | "> - ✒️ The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule." 32 | ); 33 | } 34 | if (rule.deprecated) { 35 | const replace = rule.replacedBy.map( 36 | ruleId => `[${ruleId}](./${ruleId.replace(`${pluginId}/`, "")}.md)` 37 | ); 38 | const replaceText = 39 | replace.length === 0 40 | ? "" 41 | : ` Use ${listFormatter.format(replace)} instead.`; 42 | 43 | lines.push(`> - ⛔ This rule has been deprecated.${replaceText}`); 44 | } 45 | lines.push("", ""); 46 | 47 | return lines.join("\n"); 48 | } 49 | 50 | /** 51 | * Render the document header of a given rule. 52 | * @param {RuleInfo} rule The rule information. 53 | * @returns {string} The document header. 54 | */ 55 | function renderFooter(rule) { 56 | const docsPath = path.dirname(path.resolve(docsRoot, `${rule.name}.md`)); 57 | const rulePath = path 58 | .relative(docsPath, path.join(ruleRoot, `${rule.name}.js`)) 59 | .replace(/\\/gu, "/"); 60 | const testPath = path 61 | .relative(docsPath, path.join(testRoot, `${rule.name}.js`)) 62 | .replace(/\\/gu, "/"); 63 | 64 | return `\n\n## Implementation\n\n- [Rule source](${rulePath})\n- [Test source](${testPath})`; 65 | } 66 | 67 | for (const rule of rules) { 68 | const filePath = path.resolve(docsRoot, `${rule.name}.md`); 69 | const original = fs.readFileSync(filePath, "utf8"); 70 | const body = original.replace(headerPattern, "").replace(footerPattern, ""); 71 | const content = `${renderHeader(rule)}${body}${renderFooter(rule)}\n`; 72 | 73 | fs.writeFileSync(filePath, content); 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | This is a trial of [GitHub Repository Template](https://github.blog/2019-06-06-generate-new-repositories-with-repository-templates/). 3 | 4 | Please update `package.json` after you created new repository with this template. 5 | 6 | **File Structure**: 7 | 8 | - `docs/rules/` is the directory to put documentation. 9 | - `lib/rules/` is the directory to put rule definitions. 10 | - `scripts/` is the directory to put development scripts. 11 | - `tests/lib/` is the directory to put tests for `lib/`. 12 | - `.eslintignore` and `.eslintrc.js` are the configuration to lint this repository. 13 | 14 | **Dependencies**: 15 | 16 | This template uses [mocha](https://www.npmjs.com/package/mocha), [nyc](https://www.npmjs.com/package/nyc), and [Travis CI](https://travis-ci.com/) for tests, as same as ESLint itself. If you want to use other tools, customize it. 17 | 18 | **Development Tools**: 19 | 20 | - `npm run add-rule foo` command adds a rule definition. 21 | - `npm version` command updates the following stuff by the `meta` property of rules: 22 | - the header of `docs/rules/*.md`. 23 | - `lib/configs/recommended.js` file. 24 | - `lib/index.js` file. 25 | - the rule table in `README.md` file. 26 | 27 | Below is an example of README. 28 | 29 | ---- 30 | 31 | # eslint-plugin-xxxx (template) 32 | 33 | 40 | 41 | A template for ESLint plugins. 42 | 43 | ## Installation 44 | 45 | Use [npm](https://www.npmjs.com/) or a compatibility tool to install. 46 | 47 | ``` 48 | $ npm install --save-dev eslint eslint-plugin-xxxx 49 | ``` 50 | 51 | ### Requirements 52 | 53 | - Node.js v8.10.0 or newer versions. 54 | - ESLint v5.16.0 or newer versions. 55 | 56 | ## Usage 57 | 58 | Write your config file such as `.eslintrc.yml`. 59 | 60 | ```yml 61 | plugins: 62 | - xxxx 63 | rules: 64 | xxxx/example-rule: error 65 | ``` 66 | 67 | See also [Configuring ESLint](https://eslint.org/docs/user-guide/configuring). 68 | 69 | ## Configs 70 | 71 | - `xxxx/recommended` ... enables the recommended rules. 72 | 73 | ## Rules 74 | 75 | 76 | ### Stylistic Issues 77 | 78 | | Rule ID | Description | | 79 | |:--------|:------------|:--:| 80 | | [xxxx/example-rule](./docs/rules/example-rule.md) | An example rule. | ⭐️ | 81 | 82 | 83 | 84 | ## Semantic Versioning Policy 85 | 86 | This plugin follows [Semantic Versioning](http://semver.org/) and [ESLint's Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy). 87 | 88 | ## Changelog 89 | 90 | - [GitHub Releases]() 91 | 92 | ## Contributing 93 | 94 | Welcome your contribution! 95 | 96 | See also [ESLint Contribution Guide](https://eslint.org/docs/developer-guide/contributing/). 97 | 98 | ### Development Tools 99 | 100 | - `npm test` runs tests and measures coverage. 101 | - `npm version ` updates the package version. And it updates `lib/configs/recommended.js`, `lib/index.js`, and `README.md`'s rule table. See also [npm version CLI command](https://docs.npmjs.com/cli/version). 102 | - `npm run add-rule ` creates three files to add a new rule. 103 | --------------------------------------------------------------------------------