|
| 1 | +# AI Agent Instructions |
| 2 | + |
| 3 | +This file provides guidance to AI agents when working with code in this repository. |
| 4 | + |
| 5 | +## AI Disclosure Requirement |
| 6 | + |
| 7 | +ESLint's [AI Usage Policy](docs/src/contribute/ai-policy.md) requires that AI-assisted contributions be disclosed. Whenever you (an AI) create content on GitHub for this project, you **must** include a disclosure, using the name of the model actually producing the content (for example, `Claude Opus 5`): |
| 8 | + |
| 9 | +- **Issues** — add a bold sentence to the top of body: |
| 10 | + |
| 11 | + **This issue was created with AI (Claude Opus 5).** |
| 12 | + |
| 13 | +- **Pull requests** — add a bold sentence to the top of the description: |
| 14 | + |
| 15 | + **This pull request was created with AI (Claude Opus 5).** |
| 16 | + |
| 17 | +- **Comments, review comments, and review summaries** — add a final paragraph in bold: |
| 18 | + |
| 19 | + **Created by AI (Claude Opus 5).** |
| 20 | + |
| 21 | +This applies to every AI-authored submission without exception, including follow-up comments on an existing thread. Note two related policy points: AI-generated PRs are only considered for issues labeled `accepted`, and maintainer feedback is expected to be answered by a human rather than fed back into an AI. |
| 22 | + |
| 23 | +## Commands |
| 24 | + |
| 25 | +```bash |
| 26 | +npm test # full suite: rule-file checks, mocha + coverage thresholds, fuzzer, license check |
| 27 | +npm run test:cli tests/lib/rules/no-undef.js # run a single test file (alias for local mocha) |
| 28 | +npm run lint # lint everything (JS, docs JS, docs Markdown, rule types, config files) |
| 29 | +npm run lint:fix |
| 30 | +npm run fmt # prettier --write . (prettier is the formatter; eslint does not handle style here) |
| 31 | +npm run test:types # tsc against tests/lib/types |
| 32 | +npm run test:browser # cypress against the webpack bundle |
| 33 | +npm run test:performance |
| 34 | +``` |
| 35 | + |
| 36 | +Useful details: |
| 37 | + |
| 38 | +- Coverage gates are enforced in `npm test` (99% statements/functions/lines, 98% branches). A change that lowers coverage below those thresholds fails the build even if all tests pass. |
| 39 | +- Mocha's default timeout is 10000ms; override with `ESLINT_MOCHA_TIMEOUT=20000 npm test`. |
| 40 | +- `npm test` runs mocha with `--forbid-only`, so `only: true` / `RuleTester.only(...)` must be removed before pushing. |
| 41 | +- Task definitions live in `Makefile.js` (shelljs-based), not a Makefile. `npm run lint`, `npm test`, etc. are thin wrappers around `node Makefile.js <target>`. |
| 42 | +- The docs website is a separate workspace with its own scripts: `cd docs && npm start` serves it locally. |
| 43 | +- A `lint-staged` pre-commit hook regenerates derived files. Editing `lib/rules/*.js` regenerates `packages/js/src/configs/*.js` and `lib/types/rules.d.ts`; editing `docs/src/rules/*.md` regenerates `docs/src/_data/further_reading_links.json`. Don't hand-edit those generated files. |
| 44 | + |
| 45 | +## Architecture |
| 46 | + |
| 47 | +Beyond `lib/` (the source) and `tests/` (which mirrors it), the top-level directories are `bin/` (CLI entry point), `conf/` (configuration data), `docs/` (the documentation website), `messages/` (verbose text for certain runtime errors), `packages/` (separately published packages), `templates/` (templates for generated files), and `tools/` (build, release, and check scripts). |
| 48 | + |
| 49 | +The layering is strict, and each layer is forbidden from doing what the layer below it does. Respect these boundaries — tests and reviews enforce them. |
| 50 | + |
| 51 | +- `bin/eslint.js` → `lib/cli.js` → `lib/eslint/eslint.js` → `lib/linter/linter.js` → `lib/rules/*.js` |
| 52 | +- **`lib/cli.js`** is the only place that reads argv, writes to the console, and sets exit codes. It may not call `process.exit()` directly. |
| 53 | +- **`lib/eslint/`** (`ESLint` class) owns all file system access: file/glob resolution, config loading, plugin and formatter loading. It must not print anything or use a formatter itself. `lib/eslint/worker.js` supports multithreaded linting. |
| 54 | +- **`lib/linter/`** (`Linter` class) is pure and synchronous: no file I/O, no console, no Node-specific APIs, no async. `verify()` parses text, traverses the AST, and emits node-type events (plus `:exit` events and code path analysis events from `lib/linter/code-path-analysis/`) that rules subscribe to. |
| 55 | +- **`lib/rules/`** rules are the most constrained layer: inspect the AST, report problems. Same prohibitions as `Linter`. |
| 56 | +- **`lib/config/`** implements flat config: `config-loader.js` finds and loads `eslint.config.js`, `flat-config-array.js` and `flat-config-schema.js` normalize and validate it, `default-config.js` supplies base values. |
| 57 | +- **`lib/languages/js/`** is the JavaScript language implementation, including `SourceCode`. ESLint's language plugin abstraction means JS is one language among potential others, so language-specific logic belongs here rather than in `Linter`. |
| 58 | +- **`lib/rule-tester/`** is `RuleTester`, a wrapper over Mocha-style globals used by essentially every rule test. |
| 59 | +- **`lib/shared/`** is cross-cutting utilities (`flags.js` for feature flags, `traverser.js`, severity/naming/serialization helpers). |
| 60 | +- **`lib/services/`** holds parser, processor, suppressions, and warning services used by `ESLint`. |
| 61 | +- **`packages/js`** (`@eslint/js`) publishes the `recommended` and `all` configs, generated from rule metadata. **`packages/eslint-config-eslint`** is the config this repo lints itself with. |
| 62 | + |
| 63 | +## Rules |
| 64 | + |
| 65 | +- Rule source: `lib/rules/<name>.js`. Test: `tests/lib/rules/<name>.js`. Docs: `docs/src/rules/<name>.md`. All three are required, and `npm test` fails if they aren't consistent. |
| 66 | +- New rules must be registered in `lib/rules/index.js`, in the alphabetically-sorted `LazyLoadingRuleMap`. |
| 67 | +- Each rule exports `{ meta, create }`. `meta` carries `type` (`problem` | `suggestion` | `layout`), `docs` (description, recommended, url), `schema` (JSON Schema for the rule's options), `fixable` (`"code"` or `"whitespace"`) / `hasSuggestions`, and `messages`. Report with `messageId`, never a raw string. |
| 68 | +- `create` receives a `context` object and returns AST visitor methods; rules analyze the AST through the visitor pattern. |
| 69 | +- Define helper functions at module scope, not inside `create`, so they aren't rebuilt per file. Factor common checks into helpers rather than recomputing them across visitors. |
| 70 | +- Fixable rules implement a fixer function that returns the corrections to apply. |
| 71 | +- Shared AST helpers live in `lib/rules/utils/ast-utils.js`. |
| 72 | +- `RuleTester` uses flat config (`languageOptions.ecmaVersion`, not `parserOptions.ecmaVersion`). |
| 73 | + |
| 74 | +### Rule documentation |
| 75 | + |
| 76 | +Rule docs use frontmatter with `title` and `rule_type`, and generally contain a description of what the rule checks, a rule details section explaining when it reports, examples, a "When Not To Use It" section, and optionally version information and further resources. |
| 77 | + |
| 78 | +Examples go in `::: incorrect` / `::: correct` containers, and each example includes its own `/*eslint rule-name: "error"*/` comment. `npm run lint:docs:rule-examples` validates that these examples actually produce (or don't produce) the reported problems. |
| 79 | + |
| 80 | +`tools/internal-rules/` contains lint rules that check ESLint's own rule files (e.g. `no-invalid-meta`). |
| 81 | + |
| 82 | +## Testing |
| 83 | + |
| 84 | +- Mocha with `const assert = require("chai").assert`. Tests mirror the source tree under `tests/`. |
| 85 | +- Test files follow the same layout as source files: `@fileoverview`/`@author` header, requirements, optional helpers, then the tests. Group `describe` blocks by class and method, and set up mock contexts and configs before the assertions that use them. |
| 86 | +- Cover expected behavior, edge cases, error handling, and deprecated APIs kept for backward compatibility. |
| 87 | +- Every new exported function and public class member needs tests, and every bug fix needs a test that fails without the fix. |
| 88 | +- Never delete existing tests, even failing ones. |
| 89 | + |
| 90 | +## Conventions |
| 91 | + |
| 92 | +- CommonJS (`"type": "commonjs"`), Node `^20.19.0 || ^22.13.0 || >=24`. |
| 93 | +- Source files follow a fixed layout: `@fileoverview`/`@author` header, requirements (imports), optional type definitions, optional helpers, then exports. Tools and scripts add a main section at the end. |
| 94 | +- Commits follow Conventional Commits without scopes: `fix:`, `feat:`, `fix!:`, `feat!:`, `docs:`, `chore:`, `build:`, `refactor:`, `test:`, `ci:`, `perf:`. Summary ≤72 characters. Reference issues in the body with `Fixes #1234` or `Refs #1234`. The PR title is checked in CI because it becomes the changelog entry. |
0 commit comments