Skip to content

ESLint ​

Principle ​

  • Every rule of every plugin is decided once, explicitly: enabled, configured, or disabled with its reason as a comment. No preset (recommended, strict…) is imported: the list is read, every rule is settled.
  • Everything is error. npm run lint uses --max-warnings=0.
  • Strictest by default, relaxed by zone: the files of eslint/rules/ describe the strictest setting (the functional core's); eslint/scopes/ relaxes or tightens by path.
  • The test eslint/config.test.ts (run by npm run test) fails when a rule is undecided, decided twice, non-existent, or when a zone touches an undecided rule.

Layout of eslint/ ​

text
eslint.config.ts          entry point: export default CONFIG
eslint/
  config.ts               assembly: ignores → language → Prettier → themes → zones
  config.test.ts          completeness audit
  settings/               data tables (layers, kinds, verbs, globs, limits…)
  rules/<plugin>/<theme>.ts   one file per theme, one constant per file
  rules/<plugin>/all.ts   list of the plugin's themes
  scopes/                 path overrides (core, shell, modules, tests, tooling, barrels)
  plugin/                 custom plugin `local/*`: rules, utilities, tests
FolderThemes
rules/core/variables, functions, complexity, control-flow, operators, objects, classes, async, errors, modules, naming, comments, regex, legacy
rules/typescript/type-safety, strictness, type-declarations, type-imports, signatures, promises, classes, extensions, naming
rules/jsdoc/presence, content, types, layout
rules/imports/resolution, dependencies, style, default-exports, module-systems
rules/unicorn/arrays, collections, iteration, strings, numbers, control-flow, functions, objects, errors, promises, modules, dom, web, platform, naming, misc
rules/functional/immutability, purity, paradigm
rules/stylistic/spacing, comments, prettier-owned
rules/unused/unused
rules/local/kinds, modules, documentation
Zone (scopes/)FilesEffect
language.ts**/*.tstyped parser, plugins, resolver
pure-layers.tssrc/math … src/ioforbids the DOM and the host
dom-layers.tssrc/render, src/interaction, playgroundrelaxes the pure statement rules
modules.tssrc/, playground/one export per file, kebab-case file named after it; no default nor optional parameter and documented type members in src/
playground.tsplayground/index.tsload-time effects allowed
barrels.tsindex.ts, rules/*/all.tslocal/barrel-exports; any number of imports
tooling.tseslint/, root *.config.ts, *.setup.ts, *.test.ts, tests/, docs/.vitepress/, .claude/hooks/, scripts/Node.js, dev dependencies, no @kind; in hooks and scripts run by Node.js (node-run), imports name the .ts file and no barrel is asked
tests.ts**/*.test.tsliteral numbers, free length, no @kind

The eslint/ folder itself keeps kebab-case files exporting one constant each; the one-export rule targets the library and the playground.

Changing a rule ​

  1. Find the theme: grep -rn '"rule-name"' eslint/rules.
  2. Change the value in that file only; if it becomes off, write the reason as a comment above it.
  3. A value taken from a table (GLOBAL_FUNCTION_CEILING, PROCEDURE_VERBS…) is changed in eslint/settings/.
  4. A path-specific exception goes to eslint/scopes/, never to rules/.
  5. npm run check.

After a plugin upgrade, npm run test lists the new rules to decide: add them to the right theme.

One-off exceptions ​

// eslint-disable-next-line rule -- justification — the justification is mandatory (unicorn/no-abusive-eslint-disable), an unused directive is an error.

Known autofix pitfall ​

unicorn/prefer-import-meta-properties replaces fileURLToPath(new URL(".", import.meta.url)) (trailing slash) with import.meta.dirname (no trailing slash): concatenated paths break. Always build paths with join().

Custom rules (local/*) ​

Code: eslint/plugin/rules/, one test file per rule.

local/require-kind ​

Every top-level function carries exactly one known @kind. ADR-0011.

local/kind-in-layer ​

The @kind is allowed in the file's layer (eslint/settings/layers.ts).

local/kind-limits ​

Lines, statements, complexity and depth bounded by @kind (eslint/settings/kinds.ts).

local/kind-naming ​

Name fitting the kind: verb for procedure, conversion for format, no vague verb for math/geometry/domain, predicate prefix ⇔ boolean return.

local/see-references ​

Every function has a @see; every @see points to an existing, verified reference (REF-*, DERIV-*, ADR-NNNN, docs/…md).

local/one-export-per-file ​

One exported value per module, the file named in kebab-case after it; a type may sit next to it only if it is part of its signature. ADR-0019.

local/folder-has-index ​

Every code folder holds an index.ts. ADR-0012.

local/barrel-exports ​

An index.ts contains only export * from "./x";, one per module, sorted. Autofixed.

local/canonical-import-path ​

Shortest legal import path: the file within the same folder, the folder otherwise, never index nor .ts. Autofixed.