├── .gitattributes ├── .markdownlint.json ├── .eslintignore ├── .markdownlintignore ├── .gitignore ├── .editorconfig ├── .eslintrc.cjs ├── plugin ├── templates │ ├── _.eslintrc.js │ ├── _plugin.js │ ├── _package.json │ └── _README.md └── index.js ├── .github └── workflows │ ├── add-to-triage.yml │ ├── ci.yml │ └── release-please.yml ├── tests ├── test-load.js ├── lib │ └── validators.js ├── app │ └── index.js ├── rule │ └── index.js └── plugin │ └── index.js ├── rule ├── templates │ ├── _doc.md │ ├── _test.js │ └── _rule.js └── index.js ├── LICENSE ├── lib └── validators.js ├── package.json ├── app └── index.js ├── README.md └── CHANGELOG.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "line-length": false 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | temp/ 3 | */templates/* 4 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | LICENSE 3 | node_modules 4 | 5 | temp 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | temp/ 3 | npm-debug.log 4 | .eslint-release-info.json 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | ecmaVersion: 2020 7 | }, 8 | extends: [ 9 | "eslint" 10 | ], 11 | env: { 12 | node: true 13 | }, 14 | overrides: [ 15 | { 16 | files: ["tests/**/*.js"], 17 | env: { mocha: true } 18 | } 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /plugin/templates/_.eslintrc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | root: true, 5 | extends: [ 6 | "eslint:recommended", 7 | "plugin:eslint-plugin/recommended", 8 | "plugin:node/recommended", 9 | ], 10 | env: { 11 | node: true, 12 | }, 13 | overrides: [ 14 | { 15 | files: ["tests/**/*.js"], 16 | env: { mocha: true }, 17 | }, 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /.github/workflows/add-to-triage.yml: -------------------------------------------------------------------------------- 1 | name: Add to Triage 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v0.4.0 14 | with: 15 | project-url: https://github.com/orgs/eslint/projects/3 16 | github-token: ${{ secrets.PROJECT_BOT_TOKEN }} 17 | labeled: "triage:no" 18 | label-operator: NOT 19 | -------------------------------------------------------------------------------- /tests/test-load.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Rule generator tests 3 | * @author Nicholas C. Zakas 4 | */ 5 | 6 | import assert from "node:assert"; 7 | 8 | // eslint-disable-next-line func-style, node/no-unsupported-features/es-syntax -- https://github.com/mysticatea/eslint-plugin-node/issues/250 9 | const importFresh = async modulePath => import(`${modulePath}?x=${new Date()}`); 10 | 11 | describe("eslint generator", () => { 12 | it("can be imported without blowing up", () => { 13 | const app = importFresh("../rule"); 14 | 15 | assert.ok(app); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /rule/templates/_doc.md: -------------------------------------------------------------------------------- 1 | # <%= desc %> (`<%= ruleId %>`) 2 | 3 | Please describe the origin of the rule here. 4 | 5 | ## Rule Details 6 | 7 | This rule aims to... 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```js 12 | 13 | // fill me in 14 | 15 | ``` 16 | 17 | Examples of **correct** code for this rule: 18 | 19 | ```js 20 | 21 | // fill me in 22 | 23 | ``` 24 | 25 | ### Options 26 | 27 | If there are any options, describe them here. Otherwise, delete this section. 28 | 29 | ## When Not To Use It 30 | 31 | Give a short description of when it would be appropriate to turn off this rule. 32 | 33 | ## Further Reading 34 | 35 | If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. 36 | -------------------------------------------------------------------------------- /plugin/templates/_plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview <%= desc %> 3 | * @author <%= userName %> 4 | */ 5 | "use strict"; 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | const requireIndex = require("requireindex"); 12 | 13 | //------------------------------------------------------------------------------ 14 | // Plugin Definition 15 | //------------------------------------------------------------------------------ 16 | 17 | <% if (hasRules) { %> 18 | // import all rules in lib/rules 19 | module.exports.rules = requireIndex(__dirname + "/rules"); 20 | <% } %> 21 | 22 | <% if (hasProcessors) { %> 23 | // import processors 24 | module.exports.processors = { 25 | // add your processors here 26 | }; 27 | <% } %> 28 | -------------------------------------------------------------------------------- /rule/templates/_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview <%= desc %> 3 | * @author <%= userName %> 4 | */ 5 | "use strict"; 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | const rule = require("../../../lib/rules/<%= ruleId %>"),<% if (target === "eslint") { %> 12 | RuleTester = require("../../../lib/testers/rule-tester"); 13 | <% } else { %> 14 | RuleTester = require("eslint").RuleTester; 15 | <% } %> 16 | 17 | //------------------------------------------------------------------------------ 18 | // Tests 19 | //------------------------------------------------------------------------------ 20 | 21 | const ruleTester = new RuleTester(); 22 | ruleTester.run("<%= ruleId %>", rule, { 23 | valid: [ 24 | // give me some code that won't trigger a warning 25 | ], 26 | 27 | invalid: [ 28 | { 29 | code: "<%- invalidCode.replace(/"/g, '\\"') %>", 30 | errors: [{ message: "Fill me in.", type: "Me too" }], 31 | }, 32 | ], 33 | }); 34 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | lint: 11 | name: Lint 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Setup Node.js 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version: 'lts/*' 19 | - name: Install dependencies 20 | run: npm ci 21 | - name: Lint files 22 | run: npm run lint 23 | test: 24 | name: Test 25 | strategy: 26 | matrix: 27 | os: [ubuntu-latest] 28 | node: [21.x, 20.x, 18.x, "18.18.0"] 29 | include: 30 | - os: windows-latest 31 | node: "lts/*" 32 | - os: macOS-latest 33 | node: "lts/*" 34 | runs-on: ${{ matrix.os }} 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: actions/setup-node@v4 38 | with: 39 | node-version: ${{ matrix.node }} 40 | - name: Install dependencies 41 | run: npm ci 42 | - name: Run tests 43 | run: npm test 44 | -------------------------------------------------------------------------------- /plugin/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-<%= pluginId %>", 3 | "version": "0.0.0", 4 | "description": "<%- desc.replace(/"/g, '\\"') %>", 5 | "keywords": [ 6 | "eslint", 7 | "eslintplugin", 8 | "eslint-plugin" 9 | ], 10 | "author": "<%- userName.replace(/"/g, '\\"') %>", 11 | "main": "./lib/index.js", 12 | "exports": "./lib/index.js", 13 | "scripts": { 14 | "lint": "npm-run-all \"lint:*\"", 15 | "lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"", 16 | "lint:js": "eslint .", 17 | "test": "mocha tests --recursive", 18 | "update:eslint-docs": "eslint-doc-generator" 19 | }, 20 | "dependencies": { 21 | "requireindex": "^1.2.0" 22 | }, 23 | "devDependencies": { 24 | "eslint": "^8.19.0", 25 | "eslint-doc-generator": "^1.0.0", 26 | "eslint-plugin-eslint-plugin": "^5.0.0", 27 | "eslint-plugin-node": "^11.1.0", 28 | "mocha": "^10.0.0", 29 | "npm-run-all": "^4.1.5" 30 | }, 31 | "engines": { 32 | "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" 33 | }, 34 | "peerDependencies": { 35 | "eslint": ">=7" 36 | }, 37 | "license": "ISC" 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ESLint Yeoman Generator 2 | Copyright JS Foundation and other contributors, https://js.foundation 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/lib/validators.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Rule validators tests 3 | * @author Mitchell Amihod 4 | */ 5 | 6 | //------------------------------------------------------------------------------ 7 | // Requirements 8 | //------------------------------------------------------------------------------ 9 | 10 | import { strictEqual } from "node:assert"; 11 | import { isPluginId, isRuleId } from "../../lib/validators.js"; 12 | 13 | //------------------------------------------------------------------------------ 14 | // Tests 15 | //------------------------------------------------------------------------------ 16 | 17 | describe("validation helpers", () => { 18 | it("some valid plugin names", () => { 19 | strictEqual(isPluginId("eslint-plugin-foo"), true); 20 | strictEqual(isPluginId("foo-bar"), true); 21 | 22 | }); 23 | 24 | it("plugin id can contain numbers", () => { 25 | strictEqual(isPluginId("eslint-plugin-e4x"), true); 26 | }); 27 | 28 | it("some valid rule names", () => { 29 | strictEqual(isRuleId("rule-foo-bar"), true); 30 | strictEqual(isRuleId("foo-bar"), true); 31 | }); 32 | 33 | it("rule id can contain numbers", () => { 34 | strictEqual(isRuleId("rule-1234"), true); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /plugin/templates/_README.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-<%= pluginId %> 2 | 3 | <%= desc %> 4 | 5 | ## Installation 6 | 7 | You'll first need to install [ESLint](https://eslint.org/): 8 | 9 | ```sh 10 | npm i eslint --save-dev 11 | ``` 12 | 13 | Next, install `eslint-plugin-<%= pluginId %>`: 14 | 15 | ```sh 16 | npm install eslint-plugin-<%= pluginId %> --save-dev 17 | ``` 18 | 19 | ## Usage 20 | 21 | Add `<%= pluginId %>` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix: 22 | 23 | ```json 24 | { 25 | "plugins": [ 26 | "<%= pluginId %>" 27 | ] 28 | } 29 | ``` 30 | 31 | <% if (hasRules) { %> 32 | Then configure the rules you want to use under the rules section. 33 | 34 | ```json 35 | { 36 | "rules": { 37 | "<%= pluginId %>/rule-name": 2 38 | } 39 | } 40 | ``` 41 | 42 | <% } %> 43 | 44 | ## Configurations 45 | 46 | 47 | TODO: Run eslint-doc-generator to generate the configs list (or delete this section if no configs are offered). 48 | 49 | 50 | <% if (hasRules) { %> 51 | 52 | ## Rules 53 | 54 | 55 | TODO: Run eslint-doc-generator to generate the rules list. 56 | 57 | 58 | <% } %> 59 | -------------------------------------------------------------------------------- /rule/templates/_rule.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview <%- desc %> 3 | * @author <%- userName %> 4 | */ 5 | "use strict"; 6 | 7 | //------------------------------------------------------------------------------ 8 | // Rule Definition 9 | //------------------------------------------------------------------------------ 10 | 11 | /** @type {import('eslint').Rule.RuleModule} */ 12 | module.exports = { 13 | meta: { 14 | type: null, // `problem`, `suggestion`, or `layout` 15 | docs: { 16 | description: "<%- desc.replace(/"/g, '\\"') %>", 17 | recommended: false, 18 | url: null, // URL to the documentation page for this rule 19 | }, 20 | fixable: null, // Or `code` or `whitespace` 21 | schema: [], // Add a schema if the rule has options 22 | }, 23 | 24 | create(context) { 25 | // variables should be defined here 26 | 27 | //---------------------------------------------------------------------- 28 | // Helpers 29 | //---------------------------------------------------------------------- 30 | 31 | // any helper functions should go here or else delete this section 32 | 33 | //---------------------------------------------------------------------- 34 | // Public 35 | //---------------------------------------------------------------------- 36 | 37 | return { 38 | // visitor functions for different types of nodes 39 | }; 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /lib/validators.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Validation helpers 3 | * @author Nicholas C. Zakas 4 | */ 5 | 6 | /** 7 | * Regex for valid IDs 8 | * @type {RegExp} 9 | */ 10 | const rValidId = /^(?:[a-z0-9]+(?:-[a-z0-9]+)*)$/u; 11 | 12 | /** 13 | * Determines if a given pluginId is valid. This is used by the prompt system. 14 | * @param {string} pluginId The plugin ID to check. 15 | * @returns {boolean|string} True if valid, a string with an error message if not. 16 | */ 17 | export function isPluginId(pluginId) { 18 | if (rValidId.test(pluginId)) { 19 | return true; 20 | } 21 | return "Plugin ID must be all lowercase with dashes as separators."; 22 | } 23 | 24 | /** 25 | * Determines if a given ruleId is valid. This is used by the prompt system. 26 | * @param {string} ruleId The rule ID to check. 27 | * @returns {boolean|string} True if valid, a string with an error message if not. 28 | */ 29 | export function isRuleId(ruleId) { 30 | if (rValidId.test(ruleId)) { 31 | return true; 32 | } 33 | return "Rule ID must be all lowercase with dashes as separators."; 34 | } 35 | 36 | /** 37 | * Validates that a value has been provided. This is used for validating 38 | * user input using prompts. 39 | * @param {string} value The inputted value. 40 | * @returns {string|boolean} A string if the value is an empty string, true 41 | * if the value is not an empty string. 42 | */ 43 | export function isRequired(value) { 44 | if (value === "") { 45 | return "Please provide a value"; 46 | } 47 | return true; 48 | } 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-eslint", 3 | "version": "5.0.0", 4 | "description": "A Yeoman generator for creating ESLint plugins/rules", 5 | "keywords": [ 6 | "eslint", 7 | "yeoman-generator" 8 | ], 9 | "homepage": "https://github.com/eslint/generator-eslint", 10 | "funding": "https://opencollective.com/eslint", 11 | "bugs": "https://github.com/eslint/generator-eslint/issues", 12 | "author": { 13 | "name": "ESLint", 14 | "url": "https://github.com/eslint" 15 | }, 16 | "type": "module", 17 | "exports": "./app/index.js", 18 | "files": [ 19 | "app", 20 | "lib", 21 | "plugin", 22 | "rule" 23 | ], 24 | "repository": "eslint/generator-eslint", 25 | "scripts": { 26 | "lint": "npm-run-all --continue-on-error --aggregate-output --parallel lint:*", 27 | "lint:docs": "markdownlint \"**/*.md\"", 28 | "lint:js": "eslint --report-unused-disable-directives .", 29 | "release:generate:latest": "eslint-generate-release", 30 | "release:generate:alpha": "eslint-generate-prerelease alpha", 31 | "release:generate:beta": "eslint-generate-prerelease beta", 32 | "release:generate:rc": "eslint-generate-prerelease rc", 33 | "release:publish": "eslint-publish-release", 34 | "test": "mocha --recursive tests" 35 | }, 36 | "dependencies": { 37 | "yeoman-generator": "^5.6.1" 38 | }, 39 | "devDependencies": { 40 | "eslint": "^8.19.0", 41 | "eslint-config-eslint": "^7.0.0", 42 | "eslint-release": "^3.2.0", 43 | "markdownlint-cli": "^0.31.1", 44 | "mocha": "^10.0.0", 45 | "npm-run-all": "^4.1.5", 46 | "yeoman-assert": "^3.1.1", 47 | "yeoman-environment": "^3.9.1", 48 | "yeoman-test": "^6.3.0" 49 | }, 50 | "peerDependencies": { 51 | "yo": ">=4.0.0" 52 | }, 53 | "engines": { 54 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 55 | }, 56 | "license": "MIT" 57 | } 58 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Main generator (delegates to rule or plugin generator) 3 | * @author Kevin Partington 4 | * @copyright jQuery Foundation and other contributors, https://jquery.org/ 5 | * MIT License 6 | */ 7 | 8 | //------------------------------------------------------------------------------ 9 | // Requirements 10 | //------------------------------------------------------------------------------ 11 | 12 | import Generator from "yeoman-generator"; 13 | import RuleGenerator from "../rule/index.js"; 14 | import PluginGenerator from "../plugin/index.js"; 15 | import { fileURLToPath } from "node:url"; 16 | import path from "node:path"; 17 | 18 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); // eslint-disable-line no-underscore-dangle 19 | 20 | const RULE_GENERATOR_PATH = path.join(__dirname, "..", "rule", "index.js"); 21 | const PLUGIN_GENERATOR_PATH = path.join(__dirname, "..", "plugin", "index.js"); 22 | 23 | //------------------------------------------------------------------------------ 24 | // Constructor 25 | //------------------------------------------------------------------------------ 26 | 27 | export default class extends Generator { 28 | async prompting() { 29 | const answers = await this.prompt({ 30 | type: "list", 31 | name: "outputType", 32 | message: "Do you want to generate a rule or a plugin?", 33 | choices: ["Rule", "Plugin"], 34 | default: "Rule" 35 | }); 36 | 37 | if (answers.outputType === "Rule") { 38 | this.composeWith({ Generator: RuleGenerator, path: RULE_GENERATOR_PATH }); 39 | } else if (answers.outputType === "Plugin") { 40 | this.composeWith({ Generator: PluginGenerator, path: PLUGIN_GENERATOR_PATH }); 41 | } else { 42 | throw new Error("Unhandled generator type."); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # generator-eslint ![CI](https://github.com/eslint/generator-eslint/workflows/CI/badge.svg) [![NPM version](https://img.shields.io/npm/v/generator-eslint.svg?style=flat)](https://npmjs.org/package/generator-eslint) 2 | 3 | The ESLint generator for [Yeoman](https://yeoman.io/). This generator is intended to aid development within the [ESLint](https://eslint.org/) project. It is designed to work within the top-level `eslint` directory. 4 | 5 | ## Installation 6 | 7 | First and foremost, you must have [Node.js](https://nodejs.org/) and npm installed. If you don't have Node.js installed, please download and install the latest version. 8 | 9 | > **Requirements** 10 | > 11 | > - Node.js ^14.17.0 || ^16.0.0 || >= 18.0.0 12 | 13 | You must also install Yeoman, if you don't have it installed already. To install Yeoman, you can run this command: 14 | 15 | ```sh 16 | npm i -g yo 17 | ``` 18 | 19 | With Node.js and Yeoman installed, you can run this command: 20 | 21 | ```sh 22 | npm i -g generator-eslint 23 | ``` 24 | 25 | ## Usage 26 | 27 | The Yeoman generator currently supports the following commands: 28 | 29 | ### eslint:plugin 30 | 31 | If you want to create a new ESLint plugin, make sure you're in the top-level directory where you want the plugin to be created and type: 32 | 33 | ```sh 34 | yo eslint:plugin 35 | ``` 36 | 37 | You'll be prompted for information about your plugin and it will generate a `package.json` file, README, and source code for a stub plugin. 38 | 39 | ### eslint:rule 40 | 41 | If you want to create a new ESLint rule, make sure you're in the top-level directory of an ESLint repo clone or an ESLint plugin and type: 42 | 43 | ```sh 44 | yo eslint:rule 45 | ``` 46 | 47 | You'll be prompted for some information and then it will generate the files necessary for a new rule, including the source file, a test file, and a documentation file. 48 | 49 | ## License and Copyright 50 | 51 | Copyright OpenJS Foundation and other contributors, 52 | 53 | [MIT License](https://en.wikipedia.org/wiki/MIT_License) 54 | -------------------------------------------------------------------------------- /rule/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Rule generator 3 | * @author Nicholas C. Zakas 4 | */ 5 | 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | import Generator from "yeoman-generator"; 12 | import { isRuleId, isRequired } from "../lib/validators.js"; 13 | 14 | //------------------------------------------------------------------------------ 15 | // Helpers 16 | //------------------------------------------------------------------------------ 17 | 18 | //------------------------------------------------------------------------------ 19 | // Constructor 20 | //------------------------------------------------------------------------------ 21 | 22 | export default class extends Generator { 23 | async prompting() { 24 | const prompts = [ 25 | { 26 | type: "input", 27 | name: "userName", 28 | message: "What is your name?" 29 | }, { 30 | type: "list", 31 | name: "target", 32 | message: "Where will this rule be published?", 33 | choices: [ 34 | { name: "ESLint Plugin", value: "plugin" }, 35 | { name: "ESLint Core", value: "eslint" } 36 | ] 37 | }, { 38 | type: "input", 39 | name: "ruleId", 40 | message: "What is the rule ID?", 41 | validate: isRuleId 42 | }, { 43 | type: "input", 44 | name: "desc", 45 | message: "Type a short description of this rule:", 46 | validate: isRequired 47 | }, { 48 | type: "input", 49 | name: "invalidCode", 50 | message: "Type a short example of the code that will fail:" 51 | } 52 | ]; 53 | 54 | this.answers = await this.prompt(prompts); 55 | } 56 | 57 | writing() { 58 | this.fs.copyTpl(this.templatePath("_doc.md"), this.destinationPath("docs", "rules", `${this.answers.ruleId}.md`), this.answers); 59 | this.fs.copyTpl(this.templatePath("_rule.js"), this.destinationPath("lib", "rules", `${this.answers.ruleId}.js`), this.answers); 60 | this.fs.copyTpl(this.templatePath("_test.js"), this.destinationPath("tests", "lib", "rules", `${this.answers.ruleId}.js`), this.answers); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | name: release-please 6 | jobs: 7 | release-please: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | id-token: write 13 | steps: 14 | - uses: google-github-actions/release-please-action@v3 15 | id: release 16 | with: 17 | release-type: node 18 | package-name: generator-eslint 19 | pull-request-title-pattern: 'chore: release${component} ${version}' 20 | changelog-types: > 21 | [ 22 | { "type": "feat", "section": "Features", "hidden": false }, 23 | { "type": "fix", "section": "Bug Fixes", "hidden": false }, 24 | { "type": "docs", "section": "Documentation", "hidden": false }, 25 | { "type": "build", "section": "Build Related", "hidden": false }, 26 | { "type": "chore", "section": "Chores", "hidden": false }, 27 | { "type": "perf", "section": "Chores", "hidden": false }, 28 | { "type": "ci", "section": "Chores", "hidden": false }, 29 | { "type": "refactor", "section": "Chores", "hidden": false }, 30 | { "type": "test", "section": "Chores", "hidden": false } 31 | ] 32 | - uses: actions/checkout@v3 33 | if: ${{ steps.release.outputs.release_created }} 34 | - uses: actions/setup-node@v3 35 | with: 36 | node-version: lts/* 37 | registry-url: https://registry.npmjs.org 38 | if: ${{ steps.release.outputs.release_created }} 39 | - run: npm publish --provenance 40 | env: 41 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 42 | if: ${{ steps.release.outputs.release_created }} 43 | - run: 'npx @humanwhocodes/tweet "generator-eslint ${{ steps.release.outputs.tag_name }} has been released: ${{ steps.release.outputs.html_url }}"' 44 | if: ${{ steps.release.outputs.release_created }} 45 | env: 46 | TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }} 47 | TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }} 48 | TWITTER_ACCESS_TOKEN_KEY: ${{ secrets.TWITTER_ACCESS_TOKEN_KEY }} 49 | TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} 50 | - run: 'npx @humanwhocodes/toot "generator-eslint ${{ steps.release.outputs.tag_name }} has been released: ${{ steps.release.outputs.html_url }}"' 51 | if: ${{ steps.release.outputs.release_created }} 52 | env: 53 | MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} 54 | MASTODON_HOST: ${{ secrets.MASTODON_HOST }} 55 | -------------------------------------------------------------------------------- /plugin/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Rule generator 3 | * @author Nicholas C. Zakas 4 | * @copyright jQuery Foundation and other contributors, https://jquery.org/ 5 | * MIT License 6 | */ 7 | 8 | //------------------------------------------------------------------------------ 9 | // Requirements 10 | //------------------------------------------------------------------------------ 11 | 12 | import Generator from "yeoman-generator"; 13 | import { mkdirSync } from "node:fs"; 14 | import { isPluginId, isRequired } from "../lib/validators.js"; 15 | 16 | //------------------------------------------------------------------------------ 17 | // Helpers 18 | //------------------------------------------------------------------------------ 19 | 20 | //------------------------------------------------------------------------------ 21 | // Constructor 22 | //------------------------------------------------------------------------------ 23 | 24 | export default class extends Generator { 25 | async prompting() { 26 | const prompts = [{ 27 | type: "input", 28 | name: "userName", 29 | message: "What is your name?" 30 | }, { 31 | type: "input", 32 | name: "pluginId", 33 | message: "What is the plugin ID?", 34 | validate: isPluginId 35 | }, { 36 | type: "input", 37 | name: "desc", 38 | message: "Type a short description of this plugin:", 39 | validate: isRequired 40 | }, { 41 | type: "confirm", 42 | name: "hasRules", 43 | message: "Does this plugin contain custom ESLint rules?", 44 | default: true 45 | }, { 46 | type: "confirm", 47 | name: "hasProcessors", 48 | message: "Does this plugin contain one or more processors?", 49 | default: false 50 | }]; 51 | 52 | this.answers = await this.prompt(prompts); 53 | this.answers.pluginId = this.answers.pluginId.replace("eslint-plugin-", ""); 54 | } 55 | 56 | writing() { 57 | this.fs.copyTpl(this.templatePath("_.eslintrc.js"), this.destinationPath(".eslintrc.js"), this.answers); 58 | this.fs.copyTpl(this.templatePath("_plugin.js"), this.destinationPath("lib/index.js"), this.answers); 59 | this.fs.copyTpl(this.templatePath("_package.json"), this.destinationPath("package.json"), this.answers); 60 | this.fs.copyTpl(this.templatePath("_README.md"), this.destinationPath("README.md"), this.answers); 61 | 62 | if (this.answers.hasRules) { 63 | mkdirSync(this.destinationPath("lib", "rules"), { recursive: true }); 64 | mkdirSync(this.destinationPath("tests", "lib", "rules"), { recursive: true }); 65 | } 66 | 67 | if (this.answers.hasProcessors) { 68 | mkdirSync(this.destinationPath("lib", "processors"), { recursive: true }); 69 | mkdirSync(this.destinationPath("tests", "lib", "processors"), { recursive: true }); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/app/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Main generator tests 3 | * @author Kevin Partington 4 | */ 5 | 6 | //------------------------------------------------------------------------------ 7 | // Requirements 8 | //------------------------------------------------------------------------------ 9 | 10 | import helpers from "yeoman-test"; 11 | import assert from "yeoman-assert"; 12 | import { fileURLToPath } from "node:url"; 13 | import path from "node:path"; 14 | 15 | //------------------------------------------------------------------------------ 16 | // Tests 17 | //------------------------------------------------------------------------------ 18 | 19 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); // eslint-disable-line no-underscore-dangle 20 | 21 | const APP_GENERATOR_PATH = path.join(__dirname, "..", "..", "app", "index.js"); 22 | const RULE_GENERATOR_PATH = path.join(__dirname, "..", "..", "rule", "index.js"); 23 | const PLUGIN_GENERATOR_PATH = path.join(__dirname, "..", "..", "plugin", "index.js"); 24 | 25 | describe("ESLint Main Generator", () => { 26 | describe("User answers with Plugin", () => { 27 | beforeEach(async () => { 28 | await helpers.run(APP_GENERATOR_PATH) 29 | .withPrompts({ 30 | outputType: "Plugin", 31 | userName: "John Doe", 32 | pluginId: "foo-bar", 33 | desc: "my description", 34 | hasRules: false, 35 | hasProcessors: false 36 | }) 37 | .withOptions({ "skip-install": true }) 38 | .withGenerators([ 39 | RULE_GENERATOR_PATH, 40 | PLUGIN_GENERATOR_PATH 41 | ]); 42 | }); 43 | 44 | // Just make sure the Plugin generator ran. More thorough tests are in the separate Plugin test file. 45 | it("creates expected files", () => { 46 | const expected = [ 47 | "lib/index.js", 48 | "package.json", 49 | "README.md", 50 | ".eslintrc.js" 51 | ]; 52 | 53 | assert.file(expected); 54 | }); 55 | }); 56 | 57 | describe("User answers with Rule", () => { 58 | beforeEach(async () => { 59 | await helpers.run(APP_GENERATOR_PATH) 60 | .withPrompts({ 61 | outputType: "Rule", 62 | userName: "John Doe", 63 | ruleId: "no-unused-vars", 64 | desc: "Don't include unused variables.", 65 | invalidCode: "x;", 66 | target: "plugin" 67 | }) 68 | .withOptions({ "skip-install": true }) 69 | .withGenerators([ 70 | RULE_GENERATOR_PATH, 71 | PLUGIN_GENERATOR_PATH 72 | ]); 73 | }); 74 | 75 | // Just make sure the Rule generator ran. More thorough tests are in the separate Rule test file. 76 | it("creates expected files", async () => { 77 | const expected = [ 78 | "docs/rules/no-unused-vars.md", 79 | "lib/rules/no-unused-vars.js", 80 | "tests/lib/rules/no-unused-vars.js" 81 | ]; 82 | 83 | assert.file(expected); 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /tests/rule/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Rule generator tests 3 | * @author Nicholas C. Zakas 4 | */ 5 | 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | import helpers from "yeoman-test"; 12 | import assert from "yeoman-assert"; 13 | import { fileURLToPath } from "node:url"; 14 | import path from "node:path"; 15 | 16 | //------------------------------------------------------------------------------ 17 | // Tests 18 | //------------------------------------------------------------------------------ 19 | 20 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); // eslint-disable-line no-underscore-dangle 21 | 22 | const RULE_GENERATOR_PATH = path.join(__dirname, "..", "..", "rule", "index.js"); 23 | 24 | describe("ESLint Rule Generator", () => { 25 | describe("general case", () => { 26 | beforeEach(async () => { 27 | await helpers.run(RULE_GENERATOR_PATH) 28 | .withPrompts({ 29 | userName: "John Doe", 30 | ruleId: "no-unused-vars", 31 | desc: "Don't include unused variables.", 32 | invalidCode: "x;", 33 | target: "plugin" 34 | }) 35 | .withOptions({ "skip-install": true }); 36 | }); 37 | 38 | it("creates expected files", async () => { 39 | const expected = [ 40 | "docs/rules/no-unused-vars.md", 41 | "lib/rules/no-unused-vars.js", 42 | "tests/lib/rules/no-unused-vars.js" 43 | ]; 44 | 45 | assert.file(expected); 46 | }); 47 | 48 | it("has correct rule implementation file contents", () => { 49 | assert.fileContent("lib/rules/no-unused-vars.js", "description: \"Don't include unused variables.\""); 50 | assert.fileContent("lib/rules/no-unused-vars.js", "@fileoverview Don't include unused variables."); 51 | assert.fileContent("lib/rules/no-unused-vars.js", "@author John Doe"); 52 | }); 53 | 54 | it("has correct rule doc file contents", () => { 55 | assert.fileContent("docs/rules/no-unused-vars.md", "# Don't include unused variables. (`no-unused-vars`)"); 56 | }); 57 | 58 | it("has correct rule test file contents", () => { 59 | assert.fileContent("tests/lib/rules/no-unused-vars.js", "RuleTester = require(\"eslint\").RuleTester;"); 60 | assert.fileContent("tests/lib/rules/no-unused-vars.js", "ruleTester.run(\"no-unused-vars\", rule, {"); 61 | assert.fileContent("tests/lib/rules/no-unused-vars.js", "code: \"x;\""); 62 | }); 63 | }); 64 | 65 | describe("when generating an ESLint core rule", () => { 66 | beforeEach(async () => { 67 | await helpers.run(RULE_GENERATOR_PATH) 68 | .withPrompts({ 69 | userName: "John Doe", 70 | ruleId: "no-unused-vars", 71 | desc: "Don't include unused variables.", 72 | invalidCode: "var x = \"foo\";", 73 | target: "eslint" 74 | }) 75 | .withOptions({ "skip-install": true }); 76 | }); 77 | 78 | it("has correct rule test file contents", () => { 79 | assert.fileContent("tests/lib/rules/no-unused-vars.js", "RuleTester = require(\"../../../lib/testers/rule-tester\");"); 80 | }); 81 | }); 82 | 83 | describe("With pathological input", () => { 84 | describe("Double quotes in description", () => { 85 | beforeEach(async () => { 86 | await helpers.run(RULE_GENERATOR_PATH) 87 | .withPrompts({ 88 | userName: "John Doe", 89 | ruleId: "no-unused-vars", 90 | desc: "My \"foo\"", 91 | invalidCode: "x;", 92 | target: "plugin" 93 | }) 94 | .withOptions({ "skip-install": true }); 95 | }); 96 | 97 | it("has correct description", () => { 98 | assert.fileContent("lib/rules/no-unused-vars.js", "description: \"My \\\"foo\\\"\""); 99 | }); 100 | }); 101 | 102 | describe("Double quotes in code snippet", () => { 103 | beforeEach(async () => { 104 | await helpers.run(RULE_GENERATOR_PATH) 105 | .withPrompts({ 106 | userName: "John Doe", 107 | ruleId: "no-unused-vars", 108 | desc: "Don't include unused variables.", 109 | invalidCode: "var x = \"foo\";", 110 | target: "plugin" 111 | }) 112 | .withOptions({ "skip-install": true }); 113 | }); 114 | 115 | it("has correct code snippet", () => { 116 | assert.fileContent("tests/lib/rules/no-unused-vars.js", "code: \"var x = \\\"foo\\\";\""); 117 | }); 118 | }); 119 | 120 | describe("Single quotes in description", () => { 121 | beforeEach(async () => { 122 | await helpers.run(RULE_GENERATOR_PATH) 123 | .withPrompts({ 124 | userName: "John Doe", 125 | ruleId: "no-unused-vars", 126 | desc: "My 'foo'", 127 | invalidCode: "x;", 128 | target: "plugin" 129 | }) 130 | .withOptions({ "skip-install": true }); 131 | }); 132 | 133 | it("has correct description", () => { 134 | assert.fileContent("lib/rules/no-unused-vars.js", "description: \"My 'foo'\""); 135 | }); 136 | }); 137 | 138 | describe("Single quotes in code snippet", () => { 139 | beforeEach(async () => { 140 | await helpers.run(RULE_GENERATOR_PATH) 141 | .withPrompts({ 142 | userName: "John Doe", 143 | ruleId: "no-unused-vars", 144 | desc: "Don't include unused variables.", 145 | invalidCode: "var x = 'foo';", 146 | target: "plugin" 147 | }) 148 | .withOptions({ "skip-install": true }); 149 | }); 150 | 151 | it("has correct code snippet", () => { 152 | assert.fileContent("tests/lib/rules/no-unused-vars.js", "code: \"var x = 'foo';\""); 153 | }); 154 | }); 155 | }); 156 | }); 157 | -------------------------------------------------------------------------------- /tests/plugin/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Plugin generator tests 3 | * @author Nicholas C. Zakas 4 | */ 5 | 6 | //------------------------------------------------------------------------------ 7 | // Requirements 8 | //------------------------------------------------------------------------------ 9 | 10 | import helpers from "yeoman-test"; 11 | import assert from "yeoman-assert"; 12 | import { fileURLToPath } from "node:url"; 13 | import path from "node:path"; 14 | 15 | //------------------------------------------------------------------------------ 16 | // Tests 17 | //------------------------------------------------------------------------------ 18 | 19 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); // eslint-disable-line no-underscore-dangle 20 | 21 | const PLUGIN_GENERATOR_PATH = path.join(__dirname, "..", "..", "plugin", "index.js"); 22 | 23 | describe("ESLint Plugin Generator", () => { 24 | describe("general case", () => { 25 | beforeEach(async () => { 26 | await helpers.run(PLUGIN_GENERATOR_PATH) 27 | .withPrompts({ 28 | userName: "John Doe", 29 | pluginId: "foo-bar", 30 | desc: "my description", 31 | hasRules: false, 32 | hasProcessors: false 33 | }) 34 | .withOptions({ "skip-install": true }); 35 | }); 36 | 37 | it("creates expected files", () => { 38 | const expected = [ 39 | "lib/index.js", 40 | "package.json", 41 | "README.md", 42 | ".eslintrc.js" 43 | ]; 44 | 45 | assert.file(expected); 46 | }); 47 | 48 | it("has correct package.json", () => { 49 | assert.jsonFileContent("package.json", { 50 | name: "eslint-plugin-foo-bar", 51 | author: "John Doe", 52 | description: "my description" 53 | }); 54 | }); 55 | 56 | it("has correct README.md", () => { 57 | assert.fileContent("README.md", "# eslint-plugin-foo-bar"); 58 | assert.fileContent("README.md", "Next, install `eslint-plugin-foo-bar`:"); 59 | assert.fileContent("README.md", "npm install eslint-plugin-foo-bar --save-dev"); 60 | assert.fileContent("README.md", "Add `foo-bar` to the plugins section"); 61 | 62 | assert.noFileContent("README.md", "Then configure the rules you want to use under the rules section."); 63 | }); 64 | 65 | it("has correct lib/index.js", () => { 66 | assert.noFileContent("lib/index.js", "module.exports.processors = {"); 67 | assert.noFileContent("lib/index.js", "module.exports.rules = requireIndex(__dirname + \"/rules\");"); 68 | 69 | assert.fileContent("lib/index.js", "@fileoverview my description"); 70 | assert.fileContent("lib/index.js", "@author John Doe"); 71 | }); 72 | }); 73 | 74 | describe("when rules are expected", () => { 75 | beforeEach(async () => { 76 | await helpers.run(PLUGIN_GENERATOR_PATH) 77 | .withPrompts({ 78 | userName: "John Doe", 79 | pluginId: "foo-bar", 80 | desc: "my description", 81 | hasRules: true, 82 | hasProcessors: false 83 | }) 84 | .withOptions({ "skip-install": true }); 85 | }); 86 | 87 | it("creates expected files", () => { 88 | const expected = [ 89 | "lib/rules", 90 | "tests/lib/rules", 91 | "lib/index.js", 92 | "package.json", 93 | "README.md" 94 | ]; 95 | 96 | assert.file(expected); 97 | }); 98 | 99 | it("has correct lib/index.js", () => { 100 | assert.fileContent("lib/index.js", "module.exports.rules = requireIndex(__dirname + \"/rules\");"); 101 | assert.noFileContent("lib/index.js", "module.exports.processors = {"); 102 | }); 103 | 104 | it("has correct README.md", () => { 105 | assert.fileContent("README.md", "\"foo-bar/rule-name\": 2"); 106 | }); 107 | }); 108 | 109 | describe("when processors are expected", () => { 110 | beforeEach(async () => { 111 | await helpers.run(PLUGIN_GENERATOR_PATH) 112 | .withPrompts({ 113 | userName: "John Doe", 114 | pluginId: "foo-bar", 115 | desc: "my description", 116 | hasRules: false, 117 | hasProcessors: true 118 | }) 119 | .withOptions({ "skip-install": true }); 120 | }); 121 | 122 | it("creates expected files", () => { 123 | const expected = [ 124 | "lib/processors", 125 | "tests/lib/processors", 126 | "lib/index.js", 127 | "package.json", 128 | "README.md" 129 | ]; 130 | 131 | assert.file(expected); 132 | }); 133 | 134 | it("has correct lib/index.js", () => { 135 | assert.fileContent("lib/index.js", "module.exports.processors = {"); 136 | assert.noFileContent("lib/index.js", "module.exports.rules = requireIndex(__dirname + \"/rules\");"); 137 | }); 138 | 139 | it("has correct README.md", () => { 140 | assert.noFileContent("README.md", "\"foo-bar/rule-name\": 2"); 141 | }); 142 | }); 143 | 144 | describe("With pathological input", () => { 145 | describe("With eslint-plugin prefix provided in plugin ID", () => { 146 | beforeEach(async () => { 147 | await helpers.run(PLUGIN_GENERATOR_PATH) 148 | .withPrompts({ 149 | userName: "John Doe", 150 | pluginId: "eslint-plugin-foo-bar", 151 | desc: "my description", 152 | hasRules: false, 153 | hasProcessors: false 154 | }) 155 | .withOptions({ "skip-install": true }); 156 | }); 157 | 158 | it("has correct package.json", () => { 159 | assert.jsonFileContent("package.json", { name: "eslint-plugin-foo-bar" }); 160 | }); 161 | }); 162 | 163 | describe("Double quotes in description", () => { 164 | beforeEach(async () => { 165 | await helpers.run(PLUGIN_GENERATOR_PATH) 166 | .withPrompts({ 167 | userName: "John Doe", 168 | pluginId: "foo-bar", 169 | desc: "My \"foo\"", 170 | hasRules: false, 171 | hasProcessors: false 172 | }) 173 | .withOptions({ "skip-install": true }); 174 | }); 175 | 176 | it("has correct package.json", () => { 177 | assert.jsonFileContent("package.json", { description: "My \"foo\"" }); 178 | }); 179 | }); 180 | 181 | describe("Double quotes in username", () => { 182 | beforeEach(async () => { 183 | await helpers.run(PLUGIN_GENERATOR_PATH) 184 | .withPrompts({ 185 | userName: "Kevin \"platinumazure\" Partington", 186 | pluginId: "foo-bar", 187 | desc: "my description", 188 | hasRules: false, 189 | hasProcessors: false 190 | }) 191 | .withOptions({ "skip-install": true }); 192 | }); 193 | 194 | it("has correct package.json", () => { 195 | assert.jsonFileContent("package.json", { author: "Kevin \"platinumazure\" Partington" }); 196 | }); 197 | }); 198 | 199 | describe("Single quotes in description", () => { 200 | beforeEach(async () => { 201 | await helpers.run(PLUGIN_GENERATOR_PATH) 202 | .withPrompts({ 203 | userName: "John Doe", 204 | pluginId: "foo-bar", 205 | desc: "My 'foo'", 206 | hasRules: false, 207 | hasProcessors: false 208 | }) 209 | .withOptions({ "skip-install": true }); 210 | }); 211 | 212 | it("has correct package.json", () => { 213 | assert.jsonFileContent("package.json", { description: "My 'foo'" }); 214 | }); 215 | }); 216 | 217 | describe("Single quotes in username", () => { 218 | beforeEach(async () => { 219 | await helpers.run(PLUGIN_GENERATOR_PATH) 220 | .withPrompts({ 221 | userName: "Kevin 'platinumazure' Partington", 222 | pluginId: "foo-bar", 223 | desc: "my description", 224 | hasRules: false, 225 | hasProcessors: false 226 | }) 227 | .withOptions({ "skip-install": true }); 228 | }); 229 | 230 | it("has correct package.json", () => { 231 | assert.jsonFileContent("package.json", { author: "Kevin 'platinumazure' Partington" }); 232 | }); 233 | }); 234 | }); 235 | }); 236 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [5.0.0](https://github.com/eslint/generator-eslint/compare/v4.1.5...v5.0.0) (2024-01-10) 4 | 5 | 6 | ### ⚠ BREAKING CHANGES 7 | 8 | * Require Node.js ^18.18.0 || ^20.9.0 || >=21.1.0 ([#169](https://github.com/eslint/generator-eslint/issues/169)) 9 | 10 | ### Features 11 | 12 | * add auto-generated configs list to readme in template ([#164](https://github.com/eslint/generator-eslint/issues/164)) ([93a5ffb](https://github.com/eslint/generator-eslint/commit/93a5ffb940d1be433cf0fd467e5c57d1d32b672f)) 13 | * Require Node.js ^18.18.0 || ^20.9.0 || >=21.1.0 ([#169](https://github.com/eslint/generator-eslint/issues/169)) ([6d3a572](https://github.com/eslint/generator-eslint/commit/6d3a572babe3690c62264290316daa09c679157a)) 14 | 15 | 16 | ### Chores 17 | 18 | * Bump follow-redirects from 1.15.1 to 1.15.4 ([#171](https://github.com/eslint/generator-eslint/issues/171)) ([07cbcfb](https://github.com/eslint/generator-eslint/commit/07cbcfb59543d0ab4a7d89ea26ae2c272eb3e543)) 19 | * don't install latest npm ([#168](https://github.com/eslint/generator-eslint/issues/168)) ([837bde0](https://github.com/eslint/generator-eslint/commit/837bde01473c97730bc40e8c93ca02c57aaf3bd2)) 20 | * run tests in Node.js 21 ([#167](https://github.com/eslint/generator-eslint/issues/167)) ([25a4666](https://github.com/eslint/generator-eslint/commit/25a4666e9eaad3d937b9c6f5c8701d50e6c38968)) 21 | * Update add-to-triage.yml ([#165](https://github.com/eslint/generator-eslint/issues/165)) ([5b0ea24](https://github.com/eslint/generator-eslint/commit/5b0ea24b6021c048b36f52f3e2a25eeb92c78b89)) 22 | 23 | ## [4.1.5](https://github.com/eslint/generator-eslint/compare/v4.1.4...v4.1.5) (2023-07-31) 24 | 25 | 26 | ### Chores 27 | 28 | * standardize npm script names ([#161](https://github.com/eslint/generator-eslint/issues/161)) ([b25c818](https://github.com/eslint/generator-eslint/commit/b25c8189b32f88f4b18fcd41c28dc0661c2337cd)) 29 | 30 | ## [4.1.4](https://github.com/eslint/generator-eslint/compare/v4.1.3...v4.1.4) (2023-07-27) 31 | 32 | 33 | ### Chores 34 | 35 | * Add PRs to triage ([#160](https://github.com/eslint/generator-eslint/issues/160)) ([74cfbea](https://github.com/eslint/generator-eslint/commit/74cfbead32304037db18ab142c0b5793e507e74f)) 36 | * Also post releases to Mastodon ([#152](https://github.com/eslint/generator-eslint/issues/152)) ([d0f42ab](https://github.com/eslint/generator-eslint/commit/d0f42abd433148719243e3b156c049e6f2cdaac4)) 37 | * Bump semver from 5.7.1 to 5.7.2 ([#157](https://github.com/eslint/generator-eslint/issues/157)) ([ad2d301](https://github.com/eslint/generator-eslint/commit/ad2d3016f13d63c7df8c0d5360fda493db676116)) 38 | * Bump tough-cookie and yo ([#156](https://github.com/eslint/generator-eslint/issues/156)) ([a6a2d0f](https://github.com/eslint/generator-eslint/commit/a6a2d0f4dc242bd023998598c1a306b62de21afd)) 39 | * generate provenance statements when release ([#159](https://github.com/eslint/generator-eslint/issues/159)) ([901bd7a](https://github.com/eslint/generator-eslint/commit/901bd7afa060fbd3cb02d24a21b8ccf40c2bdf03)) 40 | * include all commits in the changelog ([#154](https://github.com/eslint/generator-eslint/issues/154)) ([57fa705](https://github.com/eslint/generator-eslint/commit/57fa705cf6f61f1fc052a3f0b4169d36c8f9708f)) 41 | * run tests on Node.js v20 ([#153](https://github.com/eslint/generator-eslint/issues/153)) ([499dc15](https://github.com/eslint/generator-eslint/commit/499dc1559d1b924feebe8b0487da5aab4465855e)) 42 | 43 | ## [4.1.3](https://github.com/eslint/generator-eslint/compare/v4.1.2...v4.1.3) (2023-02-28) 44 | 45 | 46 | ### Miscellaneous Chores 47 | 48 | * test releasing with release-please ([#149](https://github.com/eslint/generator-eslint/issues/149)) ([239193a](https://github.com/eslint/generator-eslint/commit/239193a2aff6ca7bac345ce50abdade89e6e6878)) 49 | 50 | ## [4.1.2](https://github.com/eslint/generator-eslint/compare/v4.1.1...v4.1.2) (2023-02-28) 51 | 52 | 53 | ### Miscellaneous Chores 54 | 55 | * Run a test release using release-please ([#145](https://github.com/eslint/generator-eslint/issues/145)) ([6d3e535](https://github.com/eslint/generator-eslint/commit/6d3e5353c341aeb4f0b9fae6c7701d4a4a556848)) 56 | 57 | v4.1.1 - December 30, 2022 58 | 59 | * [`9349ded`](https://github.com/eslint/generator-eslint/commit/9349ded64b8f8d8f724a27a826336192411c3139) chore: Bump decode-uri-component from 0.2.0 to 0.2.2 (#141) (dependabot[bot]) 60 | * [`a810ce5`](https://github.com/eslint/generator-eslint/commit/a810ce52765b5ffe77bdee21a4a6628d5b8a9381) docs: add node version requirement to readme (#142) (Gain John) 61 | 62 | v4.1.0 - December 14, 2022 63 | 64 | * [`6878784`](https://github.com/eslint/generator-eslint/commit/68787841f22bacd6cb243fed7ca8fafb5fe4843b) feat: add eslint-doc-generator to plugin template (#140) (Bryan Mishkin) 65 | 66 | v4.0.0 - July 16, 2022 67 | 68 | * [`0c6b337`](https://github.com/eslint/generator-eslint/commit/0c6b33760347cd8035f2b37a0004d620414542f7) chore: update all dependencies (#138) (Bryan Mishkin) 69 | * [`c7de75d`](https://github.com/eslint/generator-eslint/commit/c7de75dabe32aa5baf86f9f21e7e409204641dca) feat: upgrade plugin template dependencies (#133) (Bryan Mishkin) 70 | * [`9675983`](https://github.com/eslint/generator-eslint/commit/96759838f70e37a217a4a4116e99e03b469d4a23) feat!: drop Node 12/17 support (#135) (Bryan Mishkin) 71 | * [`b6a3591`](https://github.com/eslint/generator-eslint/commit/b6a3591148f076dde927393a45c53943f89f2d57) feat: strictly define Node API in plugin template (#134) (Bryan Mishkin) 72 | * [`0a0f60c`](https://github.com/eslint/generator-eslint/commit/0a0f60cf4da05e0f8b0ad92a9ad3b79828634439) feat: drop ESLint v6 support in plugin template (#136) (Bryan Mishkin) 73 | * [`e926489`](https://github.com/eslint/generator-eslint/commit/e926489b9bd4513dc54f73e336e720167c182a13) chore: Bump tar from 6.1.0 to 6.1.11 (#125) (dependabot[bot]) 74 | * [`6650240`](https://github.com/eslint/generator-eslint/commit/6650240ce8e85b515224cba6c2624dfe3a4634d1) chore: Bump minimist from 1.2.5 to 1.2.6 (#126) (dependabot[bot]) 75 | * [`87ac39d`](https://github.com/eslint/generator-eslint/commit/87ac39d28d841f6d59a07a88b59f042e8154dc24) chore: add funding field in pkg json (#128) (Amaresh S M) 76 | * [`edda907`](https://github.com/eslint/generator-eslint/commit/edda907053284f3d8c4c9a08fc2537af70858741) fix: remove unused meta.docs.category field in rule template (#132) (Brandon Scott) 77 | * [`dff57cc`](https://github.com/eslint/generator-eslint/commit/dff57cc1adf47d57d911df079a1665478ca54aee) ci: add node v18 to test workflow (#130) (Amaresh S M) 78 | * [`e804496`](https://github.com/eslint/generator-eslint/commit/e804496e169e8830be828ba7f2df52e9d0268961) ci: update github actions (#127) (Amaresh S M) 79 | * [`58fedf1`](https://github.com/eslint/generator-eslint/commit/58fedf1aea818df481413c4f36f8a6126113ebf3) chore: recreate package-lock.json (#129) (Bryan Mishkin) 80 | * [`a74ee56`](https://github.com/eslint/generator-eslint/commit/a74ee56d3a2a3ea999538b2db87975447ac35625) chore: Bump follow-redirects from 1.14.1 to 1.14.8 (#121) (dependabot[bot]) 81 | * [`47cdfc9`](https://github.com/eslint/generator-eslint/commit/47cdfc969603abccd2c92f030f92de25dedc81a6) docs: Switch to single-line comment for rule jsdoc type (#119) (Bryan Mishkin) 82 | 83 | v3.0.1 - October 18, 2021 84 | 85 | * [`6f92f33`](https://github.com/eslint/generator-eslint/commit/6f92f334e66a2d4a005e288222dea9c93e1fc460) Chore: Use latest dependencies internally (#117) (Bryan Mishkin) 86 | * [`7f55ede`](https://github.com/eslint/generator-eslint/commit/7f55ede3f9ef1eca0b91e394a8ad30e76b70f82d) Upgrade: Use latest dependencies in plugin template (#116) (Bryan Mishkin) 87 | * [`68ba9da`](https://github.com/eslint/generator-eslint/commit/68ba9dac798264e0a4c780dd3e8681371173bbef) Chore: add type jsdoc to rule.js (#113) (eightHundreds) 88 | * [`abda0c8`](https://github.com/eslint/generator-eslint/commit/abda0c879288d108eab8eda8c68910fcc43e7b2c) Docs: Add `eslint` keyword in package.json (#115) (Bryan Mishkin) 89 | * [`7e6a5c1`](https://github.com/eslint/generator-eslint/commit/7e6a5c1a2539d52f475758d43e4776490bf60274) Docs: Add better package.json description (#114) (Bryan Mishkin) 90 | 91 | v3.0.0 - August 4, 2021 92 | 93 | * [`652d0b7`](https://github.com/eslint/generator-eslint/commit/652d0b727285bf8b38a8a79a1e893fbcd0210aa7) Docs: Update copyright (Nicholas C. Zakas) 94 | * [`ee6b480`](https://github.com/eslint/generator-eslint/commit/ee6b480b6fc0f60ea541debdb963e8ef87b1fe0d) Breaking: Convert to ESM (#112) (Bryan Mishkin) 95 | * [`f4a54b4`](https://github.com/eslint/generator-eslint/commit/f4a54b44ccbb50a0694ee4b4cb70bb4b016bf599) Chore: Catch unused ESLint comments internally (#108) (Bryan Mishkin) 96 | * [`671329e`](https://github.com/eslint/generator-eslint/commit/671329ef258bdd663a3138a578ee0d754da4b7d6) Update: Fix linting inconsistencies in template (#106) (Bryan Mishkin) 97 | * [`d5c7a80`](https://github.com/eslint/generator-eslint/commit/d5c7a80f6f533de719580ffbba21c7e379abb48c) Breaking: Upgrade yeoman dependencies to latest (#101) (Bryan Mishkin) 98 | * [`f39f2ca`](https://github.com/eslint/generator-eslint/commit/f39f2cab1a093bec3aeeb51b36d46d0541ea3616) Update: Tweak schema property formatting in rule template (#104) (Bryan Mishkin) 99 | * [`ca2a546`](https://github.com/eslint/generator-eslint/commit/ca2a546b52c355a4b7f9394b26431083400179e2) Docs: Switch to HTTPS links (#105) (Bryan Mishkin) 100 | * [`b495003`](https://github.com/eslint/generator-eslint/commit/b4950037580d7231bf2cc039011b75738cc177de) Chore: Use `npm ci` in CI workflow (#103) (Bryan Mishkin) 101 | * [`98f19d6`](https://github.com/eslint/generator-eslint/commit/98f19d655cf8d48eee460bdf79debd40502c6ac4) Chore: Fix CI to run on correct branch (#102) (Bryan Mishkin) 102 | * [`a39c90f`](https://github.com/eslint/generator-eslint/commit/a39c90f6a04ff9576c0a008b72b07f87d1748afd) Update: Move `ESLint Plugin` before `ESLint Core` in CLI prompt (#97) (Bryan Mishkin) 103 | * [`8508232`](https://github.com/eslint/generator-eslint/commit/85082323e51706843ba1a8cb942709d5372f2c97) Chore: Replace deprecated `require-uncached` with `import-fresh` (#92) (Bryan Mishkin) 104 | * [`5ceb1e9`](https://github.com/eslint/generator-eslint/commit/5ceb1e99521a25b27da69034e1a67fcfc5c5764d) Chore: Update eslint-release to v3 (#96) (Bryan Mishkin) 105 | * [`f895bcd`](https://github.com/eslint/generator-eslint/commit/f895bcdd759ac5826f76bd2338c1a63f388522dc) Update: Add placeholder for `meta.docs.url` in rule template (#99) (Bryan Mishkin) 106 | * [`3fe65c2`](https://github.com/eslint/generator-eslint/commit/3fe65c223691db85ba0e7b3bce655a54e6027f85) Update: Add placeholder for `meta.type` in rule template (#98) (Bryan Mishkin) 107 | * [`6d3e1e2`](https://github.com/eslint/generator-eslint/commit/6d3e1e2f8fcfb9624ac8c9b3575a6c0dda6aa8f2) Fix: Use caret range for `requireindex` in template (#82) (Bryan Mishkin) 108 | * [`baa4faa`](https://github.com/eslint/generator-eslint/commit/baa4faa2ec5341331b3223912520b6bd49ad2567) Chore: Remove unused dependency `run-async` (#94) (Bryan Mishkin) 109 | * [`34edf58`](https://github.com/eslint/generator-eslint/commit/34edf58581332cf574bf53d8258c3d86d2a3c951) Chore: Add markdownlint for doc formatting (#91) (Bryan Mishkin) 110 | * [`4bf02cc`](https://github.com/eslint/generator-eslint/commit/4bf02cc2f31d5c45d88106f16668c0e5e7c46019) Chore: Remove unnecessary dependency mkdirp (#93) (Bryan Mishkin) 111 | * [`866d240`](https://github.com/eslint/generator-eslint/commit/866d240f2d802e71d82465b9c61f52468b712688) New: Add eslint-plugin-node to plugin generator (#89) (Bryan Mishkin) 112 | * [`232e772`](https://github.com/eslint/generator-eslint/commit/232e77217ac81390b82661b7e5f52db70e1280d7) Chore: Update testing dependencies to latest (#90) (Bryan Mishkin) 113 | * [`7714ede`](https://github.com/eslint/generator-eslint/commit/7714ede796255428d53da8944469b1174a555608) New: Add eslint-plugin-eslint-plugin to plugin generator (#79) (Bryan Mishkin) 114 | * [`f0e6ca5`](https://github.com/eslint/generator-eslint/commit/f0e6ca5eff2950f4b74cc5afd703a0533b422e95) Docs: Fix CI build badge in README (#87) (Bryan Mishkin) 115 | * [`6d0cc94`](https://github.com/eslint/generator-eslint/commit/6d0cc94e7aaaa379e67359ccaf96953e1e05a5a6) Chore: Update and autofix internal linting (#88) (Bryan Mishkin) 116 | * [`dc4a493`](https://github.com/eslint/generator-eslint/commit/dc4a493cfa5a47e9bfe0adf34bc07858e40355a5) Breaking: Drop Node 10 and other unmaintained versions (#81) (Bryan Mishkin) 117 | * [`f761f7e`](https://github.com/eslint/generator-eslint/commit/f761f7eb002a3308d8cee6726292c5d81e81db0e) Update: Drop Node 10 from generated plugin template (#83) (Bryan Mishkin) 118 | * [`3098c58`](https://github.com/eslint/generator-eslint/commit/3098c5874ea1bd22493710edc577049dfd0cae22) Update: Add ESLint 6+ peer dependency to generated plugin template (#84) (Bryan Mishkin) 119 | * [`087a0e1`](https://github.com/eslint/generator-eslint/commit/087a0e1901cee285096fd6784e60ef33e9db45a4) Update: Bump mocha to v9 in generated plugin template (#85) (Bryan Mishkin) 120 | * [`2ff9a18`](https://github.com/eslint/generator-eslint/commit/2ff9a18a9838c71e51cf30003977899f94319300) Upgrade: Bump hosted-git-info from 2.8.8 to 2.8.9 (#77) (dependabot[bot]) 121 | * [`01c1985`](https://github.com/eslint/generator-eslint/commit/01c1985bd0066e64bf5ba3852fc58265baf96169) Docs: Add NPM version badge to README (#80) (Bryan Mishkin) 122 | * [`e3f384c`](https://github.com/eslint/generator-eslint/commit/e3f384c1ebec9cec9e7836e8a659bb15f6147e3b) Upgrade: Bump set-getter from 0.1.0 to 0.1.1 (#78) (dependabot[bot]) 123 | * [`8ce0541`](https://github.com/eslint/generator-eslint/commit/8ce05419db369e62bf199fbe39e8f341bc8cff6f) Upgrade: Bump bl from 1.2.2 to 1.2.3 (#76) (dependabot[bot]) 124 | * [`b8ee731`](https://github.com/eslint/generator-eslint/commit/b8ee731c35633c40f732177ccdddc5416036775d) Upgrade: Bump ini from 1.3.5 to 1.3.8 (#75) (dependabot[bot]) 125 | * [`8a175b6`](https://github.com/eslint/generator-eslint/commit/8a175b6c29804ef0ac3e7e844be2bc43896349c6) Chore: move to GitHub Actions (#72) (Kai Cataldo) 126 | 127 | v2.0.0 - June 8, 2020 128 | 129 | * [`520cc84`](https://github.com/eslint/generator-eslint/commit/520cc84aa5916e34264b46e159663f219170145d) Chore: Updated the template devDependencies to fix vulnerabilities. (#71) (Ron Perris) 130 | * [`6900c62`](https://github.com/eslint/generator-eslint/commit/6900c629e27a4966c6e96f0e3b843622c0060bd9) Docs: remove global-installed usage (#69) (薛定谔的猫) 131 | * [`c07f9d6`](https://github.com/eslint/generator-eslint/commit/c07f9d64aa7af7688ef9fb4ff430dae290866317) Breaking: drop support node < 8 (#68) (薛定谔的猫) 132 | * [`f591143`](https://github.com/eslint/generator-eslint/commit/f5911434cde5289dc46715d97b2ba500ec800b85) Chore: Use SVG badge in README for readability (#67) (Olle Jonsson) 133 | * [`51d9529`](https://github.com/eslint/generator-eslint/commit/51d9529404fadf1343e477910fce0240f3d0a0fc) Upgrade: eslint-release@1.0.0 (#66) (Teddy Katz) 134 | * [`43e6fe2`](https://github.com/eslint/generator-eslint/commit/43e6fe26cca2f19628bb94e34e4430710bad9918) Build: Add appveyor.yml, testing against Node 7 (#60) (Kevin Partington) 135 | 136 | v1.2.0 - December 16, 2016 137 | 138 | * d9a75e1 Chore: Fix license field in package.json to be valid SPDX string (#57) (Kevin Partington) 139 | * e7e14c1 Build: Test in Node 6 (and 7) on Travis (#56) (Kevin Partington) 140 | * 35e759c Update: Generated rule doc uses core ESLint example headers (fixes #21) (#55) (Kevin Partington) 141 | * dab779a Build: Pinning run-async dependency to 2.2.x (#54) (Kevin Partington) 142 | * 9f6f865 Update: Plugin package.json template devDependencies upgrade (fixes #52) (#53) (Kevin Partington) 143 | * 6603484 Docs: Update license copyright (Nicholas C. Zakas) 144 | * 737a81b Docs: clarify that Yeoman needs to be installed separately (#50) (Teddy Katz) 145 | * 5f990e5 Chore: Use require-uncached to avoid stale cache in tests (fixes #44) (#49) (Kevin Partington) 146 | * 2cb110f Upgrade: eslint-release@^0.10.1 (#47) (Kevin Partington) 147 | * 34f37a0 1.1.1 (ESLint Jenkins) 148 | * 1aa7f48 Build: package.json and changelog update for 1.1.1 (ESLint Jenkins) 149 | * c19cd6d Chore: Reorganizing tests to align with project structure (#46) (Kevin Partington) 150 | * 0b540e2 Fix: Double quotes correctly escaped in JS/JSON files (fixes #42) (#45) (Kevin Partington) 151 | 152 | v1.1.1 - September 30, 2016 153 | 154 | * c19cd6d Chore: Reorganizing tests to align with project structure (#46) (Kevin Partington) 155 | * 0b540e2 Fix: Double quotes correctly escaped in JS/JSON files (fixes #42) (#45) (Kevin Partington) 156 | 157 | v1.1.0 - August 10, 2016 158 | 159 | * adffe8c Update: Rule generator uses new rule format (fixes #36) (#41) (Kevin Partington) 160 | * 19417c4 New: Main generator (calls existing generators) (fixes #31) (#40) (Kevin Partington) 161 | * 314f47a Build: Add CI release scripts (Nicholas C. Zakas) 162 | * 17a0537 Chore: Rename .eslintrc, add tests eslintrc w/ mocha env (fixes #37) (#38) (Kevin Partington) 163 | * 2a37eb5 Fix: add mocha as a dependency (fixes #32) (Patrick McElhaney) 164 | 165 | v1.0.4 - April 22, 2016 166 | 167 | * 5cfbe55 Chore: Remove unnecessary copyright/license info (Nicholas C. Zakas) 168 | * 1231130 Chore: Add jQuery Foundation copyright (Nicholas C. Zakas) 169 | * 929104d Upgrade: Update dependencies, fix deprecation warnings (fixes #17) (Ian Christian Myers) 170 | * a6a4096 Fix: Update eslint dep in generated package.json (fixes #14) (Ian Christian Myers) 171 | * 7ba630b Build: Lint generator-eslint with eslint (fixes #26) (Ian Christian Myers) 172 | * 90a2425 Fix: npm test script on plugin generator (fixes #25) (Ian Christian Myers) 173 | 174 | v1.0.3 - March 10, 2016 175 | 176 | * ffa83c6 Build: Add eslint-release (Nicholas C. Zakas) 177 | * 855fd77 Fix: Complete path for correct plugin install (fixes #19) (Afnan Fahim) 178 | --------------------------------------------------------------------------------