typescript-eslint/typescript-eslint
github.com/typescript-eslint/typescript-eslint ↗ · 4000 files · 12.6 MB · scan capped
typescript-eslint Monorepo for typescript-eslint: powerful static analysis for JavaScript and TypeScript 👇 See typescript-eslint.io for documentation on the latest released version.
Overview
typescript-eslint connects ESLint’s rule engine to TypeScript syntax, scopes, and compiler type information. Its published packages serve application developers configuring linting and tool authors building parsers, rules, and tests; the repository also contains a Docusaurus documentation site with an interactive browser playground. This brief examines commit effa652828afba0aa730f106cb326c54a97ccfb5 through bounded, read-only source inspection, including configuration startup, typed linting, and playground editing and persistence.
System map
The repository distributes a set of cooperating linting libraries and a separate documentation application. ESLint supplies the execution environment; the parser supplies ESTree, scopes, and optional compiler services. Nx coordinates package builds, checks, and releases across the pnpm workspace. Core parser state resides in process memory, while the playground uses virtual files and browser persistence.
- The root scripts delegate build, lint, unit tests, integration tests, and both type-checking variants to Nx; ordinary build and test commands exclude the website packages.
- The public parser ships CommonJS JavaScript and declarations from dist, depends on typescript-estree, scope-manager, types, and visitor-keys, and declares ESLint 8.57/9/10 compatibility.
- typescript-eslint exposes a parser adapter, the eslint-plugin object, preset getters, configuration helpers, and file-extension/glob helpers through its package entrypoint.
- raw-plugin.ts owns the rule registry and legacy presets, constructs flat presets using the shared plugin/parser objects, then attaches those presets to plugin.configs.
- parseForESLint returns four ESLint integration objects: ast, scopeManager, services, and visitorKeys; compiler options can supply library and JSX analysis settings.
- typescript-estree keeps legacy single-run Programs and default-project matches in module-level collections; createParseSettings separately caches one ProjectService instance.
- The website is a private React/Docusaurus package; its configuration mounts rule documentation at /rules and repository documentation at the root, with an Algolia search integration.
- Release automation selects canary publishing after successful main CI, scheduled stable publishing, or manual releases; publishing jobs use npm-registry environments and OIDC permissions, while stable releases also update the website branch.
Feature catalog
The primary user surface is ESLint configuration and diagnostics, supported by reusable parser and rule-author APIs. Presets distinguish correctness, strictness, style, and type-information requirements. The browser playground adds editable configurations, diagnostic markers, structural inspection, and shareable examples. The following catalog groups features found in inspected files rather than claiming exhaustive coverage of every rule.
- Users can select recommended, strict, and stylistic presets, their type-checked and type-checked-only variants, all, base, eslintRecommended, or disableTypeChecked.
- The deprecated config() helper flattens nested arrays and expands object-based extends; it rejects string extensions, nested extends, and basePath inside extensions.
- The parser exposes parse and parseForESLint alongside createProgram, clearCaches, and helpers for removing project-related parser options.
- Typed linting accepts project-service configuration, supplied Programs, or legacy project resolution; projectService and project together normally produce a configuration error.
- no-floating-promises detects unhandled Promise-like expressions, supports safe-call/type allowances and thenable/IIFE/void options, and offers await or void suggestions under applicable report branches.
- The recommended-type-checked preset enables promise, unsafe-value, assertion, unused-code, and other correctness rules while disabling corresponding core rules where replacements are configured.
- Rule authors receive RuleCreator with default-option merging and documentation URLs, plus a RuleTester that normalizes filenames and runs rules through a flat-config Linter.
- The playground exposes code, ESLint, and TypeScript configuration editing, error displays, ESTree/scope/TypeScript/type views, ESQuery filtering, and a Ctrl/Cmd-S lint-fix action.
Data flow
Configuration enters through exported presets and parser options, then determines how source text acquires a TypeScript AST and optional Program. Conversion and scope analysis produce the structures ESLint rules consume. Compiler services translate ESTree nodes back to TypeScript nodes for typed queries. The playground follows a parallel browser path through a virtual filesystem, with URL and local-storage state at its persistence boundary.
- Flat base configuration installs the parser and @typescript-eslint plugin with module sourceType; preset getters additionally record candidate tsconfig roots inferred from the caller’s stack.
- createParseSettings converts input to full text, infers single-run mode, validates an explicit absolute tsconfigRootDir, and resolves the input filename against that root.
- getProgramAndAST tries project-service first, then supplied Programs, then standalone parsing without type information, and finally watched legacy project Programs.
- astConverter rejects the first fundamental parse diagnostic, invokes Converter.convertProgram, optionally strips locations/ranges, and attaches requested token and comment arrays.
- Converter owns bidirectional WeakMaps; createParserServices returns maps even without a Program and, with a Program, translates ESTree nodes into checker calls for types, symbols, signatures, and contextual types.
- analyze creates ScopeManager and Referencer, then visits the AST; Referencer combines library definitions’ type/value flags and defines an implicit type-only const binding.
- Playground files live in a Map seeded with /.eslintrc, /tsconfig.json, and optional /input content; changed writes notify matching debounced watchers, which drive linter.verify and diagnostic events.
- useHashState merges defaults, local preferences, and URL state in that order; updates save preferences and replace the hash with compressed code/configuration, reloading the page when cfg.ts is provided.
Behavior traces
These traces follow source-level call order rather than observed executions. Configuration startup establishes shared plugin identity before ESLint parsing and rule dispatch. Typed linting adds project-service creation, file admission, and compiler lookup. Playground startup, editing, and persistence connect browser state to virtual files, lint events, and restored examples.
- Configuration startup: the package entrypoint checks TypeScript’s major version, imports pluginBase/rawPlugin, exposes their shared parser/plugin, and creates getters whose reads call getTSConfigRootDirFromStack then addCandidateTSConfigRootDir.
- Syntax-only linting: parseForESLint normalizes options and forces comments/locations/ranges/tokens → parseAndGenerateServices creates settings → getProgramAndAST uses createNoProgram → astConverter and createParserServices return structures → analyze supplies scopes.
- Typed service initialization: createParseSettings → populateProjectService validates default-project globs and initializes the cached service → createProjectService loads tsserverlibrary, constructs its host, creates ProjectService, applies cleanup throttling, and reads default compiler options.
- Typed file admission: useProgramFromProjectService updates extensions and matches allowDefaultProject → openClientFileFromProjectService opens current text, may retry a symlink path or reload stale projects, and rejects invalid admission → retrieveASTAndProgramFor obtains the language-service Program → createProjectProgram selects the AST.
- Promise diagnostic: no-floating-promises.create obtains parser services/checker → ExpressionStatement skips allowed IIFEs and unwraps chains → checkNode skips known-safe calls and calls isUnhandledPromise → context.report selects array, handler, or ordinary messages and applicable suggestions.
- Playground startup: Play renders BrowserOnly/Suspense → Playground initializes useHashState → useSandboxServices calls sandboxSingleton/loadSandbox → the loader checks the requested Monaco bundle and loads modules → the hook creates the sandbox, virtual filesystem, library files, and web linter.
- Playground edit: LoadedEditor.onDidChangeModelContent → system.writeFile → the /input watcher invokes triggerLint → linter.verify calls the virtual parser, which updates/creates the source and converts its Program AST → onLint updates Monaco markers and onParse updates AST views.
- Playground persistence: virtual-file watchers call onChange with code/eslintrc/tsconfig → updateState merges changes and skips shallow-equal updates → writeStateToLocalStorage saves preferences → writeStateToUrl encodes the example → history.replace stores the hash used by the next initialization.
Testing signals
Tests and CI were inspected, not executed; no passing result or measured coverage is asserted. Vitest supplies package suites, with RuleTester cases, parser snapshots, mocked service tests, and packed-package integrations. CI conditionally selects affected packages and adds compatibility and snapshot checks. The inspected configuration excludes website suites from the root test project, so it does not establish browser-flow coverage; the inventory remains bounded.
- The shared Vitest configuration enables globals, disables isolation and watch mode, installs console-fail-test setup, discovers test/test-d files, and configures source coverage with fixture exclusions.
- Parser service tests discover .src.ts fixtures, snapshot converted ASTs, and assert that parser services contain a Program for configured parsing paths.
- no-floating-promises tests use createRuleTesterWithTypes, include accepted handling/void examples, and assert suggestion outputs; the helper defaults typed tests to projectService unless a project was requested.
- Project-service tests mock configuration parsing and host configuration, while useProgramFromProjectService tests substitute service/program factories; these assertions exercise branch behavior without proving complete real-tsserver interoperability.
- Cleanup-throttle tests use a fake service and performance timers to check first cleanup, 249/250 ms boundaries, out-of-open cleanup, argument/this forwarding, return values, and thrown errors.
- Preset tests compare metadata-derived rule selections against all, disable-type-checked, and recommended configs; scope fixtures discover JS/TS/JSX/TSX files with per-fixture analysis options.
- Integration Vitest setup packs public packages and installs temporary fixture dependencies; ESLint integration tests run --fix-dry-run, parse JSON output, normalize paths, and compare snapshots, including an ESLint-v9 fixture.
- CI shards affected plugin tests on Linux with Node 18/24, runs a separate project-service mode, and aggregates required checks; Windows configuration skips rule suites, and the root Vitest project excludes website and website-eslint projects.
Risk map
These are source observations and explicitly labeled hypotheses, not confirmed vulnerabilities or reproduced failures. Sensitive boundaries include filesystem-backed compiler services, third-party browser code, shell-based integration tooling, and privileged workflow artifacts. Several playground call sites expose concrete inconsistencies suitable for targeted verification. Compiler internals and process-wide caches also require compatibility-focused review.
- ProjectService inherits tsserver.sys filesystem access and stubs watchers rather than confining reads to the repository; TypeScript plugin loading is blocked by default but can be enabled, and a custom host is supported.
- throttleOpenedFileCleanup replaces undocumented cleanup methods and openClientFileWithNormalizedPath; compatibility depends on TypeScript internal method names and signatures, while inspected tests use a fake service.
- The playground executes an external loader script and loads Monaco/sandbox code from TypeScript-hosted domains; loadSandbox handles HEAD/module failures but its inspected implementation installs no script onerror handler.
- Potential fix-action failure: LoadedEditor passes editor.getValue() to triggerFix, but triggerFix treats that argument as a filename and reads it from the virtual filesystem; ordinary source text therefore appears unable to select the input file. (inferred)
- applyEslintConfig pushes resolved presets into a persistent eslintExtendedConfig array without clearing it on configuration changes; the watcher repeatedly calls this function before relinting.
- Local-state restoration checks fileType against the literal 'true' although the writer stores newState.fileType; sourceType is written but not restored by the inspected reader, and localStorage.setItem is outside a try/catch.
- Apply Review Labels runs with pull-requests:write, downloads review-intent from a completed workflow, appends its contents directly to GITHUB_OUTPUT, and uses the resulting pr_number/intent outputs for label actions; the artifact boundary deserves validation review.
- Integration execution writes beneath os.tmpdir() or the home-directory fallback, packs packages and installs dependencies with shell-enabled child processes; it is materially broader than a read-only source check.
Extension points
Rules plug into an explicit registry and a typed factory API. Preset generation consumes rule metadata, while syntax support spans conversion, visitor keys, and scope visitors. Project-service hosts and RuleTester framework adapters offer controlled integration boundaries. Playground configuration and diagnostics use maps, schemas, and event callbacks rather than a server router.
- New plugin rules register in src/rules/index.ts, whose named rule map is imported into raw-plugin.ts and exposed as plugin.rules.
- The local createRule wraps ESLintUtils.RuleCreator with the repository’s rule-documentation URL; RuleCreator accepts typed metadata/options/listeners and supports withoutDocs for other consumers.
- Preset generation derives core-rule overrides from extendsBaseRule and filters requiresTypeChecking/deprecated metadata, rejecting rules that are simultaneously deprecated and recommended.
- Flat presets are factory functions receiving plugin and parser objects; adding a preset requires wiring its factory into raw-plugin’s flatConfigs map and any desired umbrella export.
- Additional AST traversal keys are statically checked against node properties and merged with eslint-visitor-keys; their arrays must follow source visitation order.
- Scope integration accepts childVisitorKeys, library, JSX, and source-type options; Referencer supplies node-specific visitors and resolves transitive library definitions through TSLibraries.
- createProjectService accepts a partial ServerHost and service options; RuleTester’s TestFramework adapter separately permits explicit afterAll, describe, and it implementations instead of globals.
- createLinter exposes rule/config maps, onLint/onParse registration, registerFile, and trigger methods; LoadedEditor derives JSON validation schemas from registered rule metadata with external schema requests disabled.
Contributor handoff
Tests and CI were inspected, not executed, and all described flows are source traces. Future agents should begin with the owning package and preserve public parser, plugin, and generated-config contracts. Test and build commands below describe repository workflows for a separately authorized implementation task; they do not authorize execution during this read-only investigation. This bounded inventory leaves additional packages, rules, fixtures, and browser behavior for task-specific inspection.
- Read the owning manifest, root scripts, and local-development guide first; the guide directs contributors to the packageManager field and explains package build dependencies.
- For authorized validation, start with the owning package’s test command, then relevant lint/typecheck checks; packed integration tests are a separate root script, and Vitest is configured without watch mode.
- Installation has substantial side effects: postinstall changes to workspaceRoot, installs Husky hooks, and normally runs clean, build, and ast-spec type checking unless skip conditions apply.
- Treat generated presets as generator outputs: their headers prohibit hand editing, and rule-development documentation identifies generate-configs and rule-doc snapshot regeneration as follow-up workflows.
- Preserve plugin identity across package imports and compatibility boundaries; parser/plugin manifests support ESLint 8.57/9/10 and TypeScript below 6.1, while parser startup explicitly rejects TypeScript major versions at least 7.
- When changing parser behavior, preserve forced ESLint tokens/comments/locations and node maps; legacy single-run repeated parsing falls back to an isolated Program, whereas projectService bypasses that fallback.
- Repository skill guidance emphasizes comment-preserving fixers, source-level type fixes, shared utilities, precise static rule tests, and deferred checker calls; visitor keys must retain source order rather than alphabetical order.
- The AI contribution policy requires close human review and sufficient independent understanding, discourages AI-generated issue/comment/PR prose, and allows maintainers to close contributions that do not follow its guidance.
Languages
| Language | Files | Share of source |
|---|---|---|
| TypeScript | 2658 | 97% |
| Markdown | 292 | docs/config |
| JSON | 134 | docs/config |
| JavaScript | 71 | 3% |
| CSS | 37 | docs/config |
| YAML | 35 | docs/config |
| Vue | 7 | 0% |
| TOML | 1 | 0% |
Start reading here
Probable source entrypoint: packages/ — inferred from the directory tree and manifest, not the upstream docs.
Source roots
packages/ (3846 files)
Source layout — packages/
| Path | Files |
|---|---|
packages/ast-spec/ | 168 |
Repository structure
| Top-level path | Files |
|---|---|
packages/ | 3846 |
docs/ | 53 |
.github/ | 39 |
tools/ | 19 |
.agents/ | 9 |
typings/ | 1 |
.husky/ | 1 |
Docs found: CONTRIBUTING.mdREADME.md
Package
package.json — @typescript-eslint/typescript-eslint, 54 declared dependencies.
@actions/github@arethetypeswrong/cli@eslint-community/eslint-plugin-eslint-comments@eslint/compat@eslint/config-helpers@eslint/js@nx/devkit@nx/js@nx/vitest@nx/workspace@swc/core@types/debug
CI & config entrypoints
README.md— Repository readme.github/workflows/a11y-alt-bot.yml— GitHub Actions workflow.github/workflows/breaking-change-validation.yml— GitHub Actions workflow.github/workflows/ci.yml— GitHub Actions workflow.github/workflows/cleanup-cache.yml— GitHub Actions workflow.github/workflows/force-push-bot.yml— GitHub Actions workflow.github/workflows/github-actions-security.yml— GitHub Actions workflow.github/workflows/nx-migrate.yml— GitHub Actions workflow.github/workflows/on-review-status-change.yml— GitHub Actions workflow.github/workflows/pr-labels-from-issue.yml— GitHub Actions workflow
Structural health signals
Average score 6.8/10 across 96 analyzed source files; 37 hotspots surfaced.
packages/scope-manager/src/referencer/Referencer.ts— 866 lines, score 5.5 (Large file, Deep nesting)packages/eslint-plugin/src/util/collectUnusedVariables.ts— 844 lines, score 5.5 (Large file, Deep nesting)packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts— 855 lines, score 5.5 (Large file, Deep nesting)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
effa652828af2026-10-02 — fix(eslint-plugin): [no-unused-vars] report usedOnlyAsType for partially exported merged declarations (#12892)9fbab431e5182026-10-01 — fix(eslint-plugin): [no-misused-promises] skip argument checks for calls without arguments (#12923)358a8168d6c02026-10-01 — docs: explain how to customize shared configs (#12954)3ab28b15dc622026-10-01 — chore(eslint-plugin): re-enable `plugin-test-formatting` in all tests (#12950)a7abcabf43292026-10-01 — chore(type-utils): increase test timeout (#12949)