Skip to content
HomePagesHomePages template kit
Menu

ESLint preset

The authoring-lint preset shipped as `@homepages/eslint-plugin-template` — setup and scope. Per-rule reference lives in `rules/`.

@homepages/eslint-plugin-template is a flat-config ESLint preset carrying every authoring rule the kit enforces one file at a time, under the template-kit/* rule namespace.

Terminal window
npm i -D @homepages/eslint-plugin-template eslint @typescript-eslint/parser
eslint.config.mjs
import kit from "@homepages/eslint-plugin-template";
export default kit;

That is the whole setup — no plugin registration, no parser wiring, no tsconfig. The preset registers its rules and sets languageOptions (parser included) itself, so those two lines are a complete config.

The preset ships as its own package rather than a kit subpath because it needs a different dependency tree: ESLint and the parser, not React. That keeps a linter out of the production install of every app that renders a template — the kit’s main entry is a set of React primitives, and it must not drag one in.

ESLint and the parser are peer dependencies, so you install them yourself; the preset resolves them from your workspace. If you see Cannot find package '@typescript-eslint/parser', that is the install line above missing.

The preset is syntactic only — it never sets parserOptions.project or projectService. Nothing it enforces requires type information, which is what keeps setup to zero extra config; a consumer’s own tsconfig.json is never consulted.

The preset is consumed once at a workspace root and lints every section nested under templates/**; there are no per-template config files. It ships three config entries, matched by glob rather than by directory depth:

Entry Files Rules
template-kit/authoring **/*.ts, **/*.tsx the eleven file-scoped rules — see rules/
template-kit/contract-files **/Renderer.tsx, **/schema.ts, **/fill-spec.ts, **/fixtures.ts no-client-directive-in-contract
template-kit/renderer **/Renderer.tsx props-from-schema

Server-vs-client scoping is decided per file, by the "use client" directive prologue — see server vs client for what that means and exactly which rules stop at that boundary.

Every rule the preset enforces — what it bans, why, and a before/after fix — is documented once, per id, in rules/. Rules scoped to a single file are enforced here, by this preset; rules scoped to the tree — a missing contract file, a slot declared in schema.ts but unmarked in Renderer.tsx, a CSS budget — cannot be expressed one-file-at-a-time and belong to template-kit check under the same template-kit/<id> namespace. One namespace, two venues: the id printed by either failure resolves to exactly one page.