@titan-design/eslint-plugin
v0.4.0
Published
ESLint rules that enforce the titan code-quality limits: functions of at most 30 non-blank lines, opt-in code lines of at most 200 characters, opt-in no wall clock in beat and motion code, comments that hold code, and TODO comments without a tracking task
Maintainers
Readme
@titan-design/eslint-plugin
ESLint rules for the titan code-quality limits. Each message tells the reader what to do next, so an agent that hits the rule can fix the code without asking.
max-function-lines: a function may have at most 30 non-blank lines (optionmax). Blank lines are not counted; comment lines are.max-line-length(opt-in, not inrecommended): a code line may have at most 200 characters (optionmax). Comment lines, import and re-export declarations, and lines where string, template or regex literals make up at least half the characters after the indent are skipped. A line holding a short literal among long logic is still reported, unlike coremax-len'signoreStrings.todo-needs-issue: everyTODOin a comment must carry its task right after the marker:TODO(TP-123),TODO(#123), or a bareTODO: TP-123/TODO #123that ends the sentence.TODO UTF-8 support,TODO ES-2022(keys with a standards prefix such asUTF,ES,ISO,RFC,SHA,MD,TLS,SSL,IPV,ECMA,HTTPare prose), and a key that sits later in the comment or inside a URL are reported.FIXMEis deliberately not checked; the rule is aboutTODO.no-commented-code: a comment whose text parses as statements with a code signal (a declaration or control statement, an assignment, a call,;) is reported; a lone expression such asread-onlyor100 - 75is prose. JSDoc and directive comments pass.no-chained-type-assertions: a type assertion applied to another one, such asx as unknown as Tor<T>(x as unknown), is reported once at the outermost assertion. A chain made only ofas constpasses. In tests, build the fake withpartialFake<T>()from@titan-design/test-kit. It reads syntax only, so it cannot see a double cast split across two statements (const u: unknown = x; u as T); type-aware@typescript-eslint/no-unsafe-type-assertioncovers that.no-internal-module-mock:vi.mock,vi.doMock,jest.mock,jest.doMockandjest.unstable_mockModulemay not target this repo's own code: a relative, absolute or#specifier, or one under an internal prefix (optioninternalPrefixes, default["@titan-design/"]).node:builtins and third-party packages stay mockable. It matches theviandjestnames only, so a renamed import (import { vi as t }) is not seen, and it skips a specifier built at run time.no-wall-clock-motion(opt-in, not inrecommended): in beat and motion code,Date.now,performance.now,Math.random,setTimeout,setInterval,setImmediateandrequestAnimationFrameare reported, bare or behindwindow,selforglobalThis, so a seek or a second render shows the same frames. It applies only to files whose path contains one of the optionpaths(default["/ui/beats/", "/motion/"]) and never to*.test.*,*.spec.*or__tests__/files. It reads call syntax only, so an aliased import (const now = Date.now) is not seen.no-test-clock-budget(opt-in, not inrecommended): in a test file, anexpect(x)compared withtoBeLessThan,toBeGreaterThanor theirOrEqualforms is reported whenxcomes from a clock (Date.now,performance.now,process.hrtime,process.cpuUsage,process.threadCpuUsage), followed through arithmetic and local names and functions by scope. A loaded CI runner misses such a limit or ratio; assert counted work instead, or gate the timing on a condition that names a bench flag. It cannot follow a time through an imported helper (TP-4262).recipe-step-in-loop(opt-in, not inrecommended):ctx.dispatch,ctx.seed,ctx.assisted,ctx.authorize,step(ctx, …)andcodeStep(ctx, …)are reported inside afor,whileordoloop or amap/forEach/flatMap/reduce/filter/some/everycallback, unless it is in a.mapor.flatMapcallback written directly as an argument of anall(...)that is itself in no loop (a loop deeper in that callback is still reported). AmapItemscallback is a fan-out, not a loop. It matches the namectxand reads one function at a time, so a helper that holds the step op and is called in a loop is not seen; the runtime orphan trap (TP-3108 S5) covers that.recommended: a flat config enabling the five rules other thanmax-line-length,no-test-clock-budget,no-wall-clock-motionandrecipe-step-in-loopaterrorunder thetitannamespace.
Tier 0 of the titan-platform DAG. May import only packages in the same tier or
below; the package-layers rule in .codewatch/check.json enforces this in CI.
eslint 9 is a peer dependency.
// eslint.config.js
import titan from "@titan-design/eslint-plugin";
export default [
{
plugins: { titan },
rules: { "titan/max-function-lines": "error", "titan/todo-needs-issue": "error" },
},
];Status: unpublished (private). The first publish follows in a later TP-897 slice.
