| name | ksc |
| description | Rules and conventions for projects using @kitschpatrol/shared-config. Covers ESLint, Prettier, TypeScript, CSpell, Knip, Stylelint, and Perfectionist configuration to avoid lint failures. Relevant when writing or modifying TypeScript, JavaScript, CSS, Markdown, JSON, YAML, or configuration files. |
| user-invocable | true |
@kitschpatrol/shared-config Rules
This project uses @kitschpatrol/shared-config for linting, formatting, and type checking. All tools are orchestrated via the ksc CLI. Always use pnpm — never npm, npx, or yarn. After writing code, run pnpm ksc fix to auto-fix formatting, sorting, and other fixable issues.
Critical Type Rules
These cause the most lint failures. Follow them strictly:
- No
null — Use undefined instead. Both the value null and the type null are banned (unicorn/no-null, ts/no-restricted-types). When interfacing with APIs that return null, convert immediately: apiResult() ?? undefined
- No
any — Use unknown instead (ts/no-explicit-any). Also enforced: ts/no-unsafe-assignment, ts/no-unsafe-call, ts/no-unsafe-member-access, ts/no-unsafe-return, ts/no-unsafe-type-assertion
- No
Buffer — Use Uint8Array instead
- No
object type — Use Record<string, unknown> instead
- Handle all promises —
ts/no-floating-promises is an error. Async functions returning promises must use await. Use void someAsyncFunction() for intentionally fire-and-forget calls. Functions that return a promise must be declared async (ts/promise-function-async)
- Strict template expressions — Only strings and numbers are allowed in template literals (
ts/restrict-template-expressions with allowNumber: true). Booleans, objects, nullish values must be converted explicitly: `flag: ${String(isReady)}`
- Strict plus operands — Cannot implicitly add string + number (
ts/restrict-plus-operands)
- Prefer nullish coalescing — Use
?? not || for nullish checks (ts/prefer-nullish-coalescing). Use ?. optional chains (ts/prefer-optional-chain)
- Check indexed access —
noUncheckedIndexedAccess is enabled: array[i] and record[key] are typed T | undefined. Narrow before use
Naming Conventions
- Default:
camelCase for variables, functions, parameters
- Types:
StrictPascalCase for types, interfaces, enums, classes, type parameters
- Constants:
const variables may be UPPER_CASE or camelCase
- Booleans: Variables typed as
boolean MUST be prefixed with is, has, can, should, will, or did (e.g., isReady, hasPermission)
- Unused variables/parameters: MUST have a leading underscore prefix (
_unused, _error)
- Imports:
camelCase or StrictPascalCase (for component imports)
- Destructured variables:
camelCase or StrictPascalCase allowed
- File names:
kebab-case enforced by unicorn/filename-case
- Prefer
type over interface: ts/consistent-type-definitions enforces type Foo = { ... } not interface Foo { ... }
- Type imports/exports: Use top-level style:
import type { Foo } from './bar' and export type { Foo } — inline specifiers like import { type Foo } are flagged (import/consistent-type-specifier-style with prefer-top-level)
- Array types: Simple types use
string[], complex types use Array<string | number> (ts/array-type with array-simple)
Sorting — Do Not Manually Sort
The Perfectionist plugin auto-sorts imports, object keys, type members, union/intersection types, and interfaces. All these rules have auto-fix. Do not waste time manually sorting — write code in any logical order, then run pnpm ksc-eslint fix or pnpm ksc fix.
Perfectionist sorts alphabetically (natural, ascending) with domain-specific exceptions that preserve semantic grouping:
- Coordinates:
x, y, z, w and x1, y1, x2, y2
- Dimensions:
width, height
- Colors:
r, g, b, a / red, green, blue, alpha / h, s, l, a
- Ranges:
min, max / start, end / open, close / pre, post
These patterns are matched by exact name, prefix, and suffix. If an object's keys all match one of these patterns, the domain order takes precedence over alphabetical.
Import sorting: alphabetical with no newlines between groups. Internal modules (~/, @/, $) are grouped together.
Code Style
Formatting is handled by Prettier — do not fight it:
- No semicolons
- Single quotes
- Tabs for indentation (except
.md, .mdx, .yml which use spaces)
- Trailing commas always
- Print width: 100 characters
- LF line endings
Additional ESLint style rules:
- Always use braces —
if/else/loops require braces even for single statements (curly)
- Blank line after blocks — A blank line is required between a block-like statement (
if, for, while, try, function declarations, etc.) and the next statement (stylistic/padding-line-between-statements, auto-fixed)
- Comments must be capitalized (
capitalized-comments) — exception for commented-out code patterns like if, else, const, import, etc.
- No
Array.reduce() — Use for...of loops instead (unicorn/no-array-reduce)
- No
Array.forEach() — Use for...of loops instead (unicorn/no-array-for-each)
- Use
node: protocol for Node.js built-in imports: import fs from 'node:fs' (unicorn/prefer-node-protocol)
- No abbreviations (
unicorn/name-replacements) except these allowed ones: i, j, acc, arg, args, db, dev, doc, docs, env, fn, lib, param, params, pkg, prop, props, ref, refs, sep, src, temp, util, utils
- Explicit length checks — Write
array.length > 0 not array.length (unicorn/explicit-length-check)
- Catch variable naming — Use
error not err or e (unicorn/catch-error-name)
JSDoc
- Public exports require JSDoc with a body description (
jsdoc/require-jsdoc with publicOnly: true, jsdoc/require-description)
- No types in JSDoc — TypeScript provides the types. Do not write
@param {string} name, just @param name (jsdoc/no-types)
- In
.ts files, @param and @returns tags are not required — the TypeScript signature is sufficient
ESLint Plugin Name Mappings
ESLint plugins are renamed in this config. Use these short names in eslint-disable comments:
@typescript-eslint -> ts (e.g., // eslint-disable-next-line ts/no-explicit-any)
import-x -> import
n -> node
vitest -> test
@eslint-community/eslint-comments -> eslint-comments
@eslint-react -> react
@stylistic -> stylistic
jsonc -> json
yml -> yaml
@html-eslint -> html
package-json -> json-package
Project Patterns and Tooling
Package manager: Always use pnpm. Never use npm, npx, or yarn. Dependencies are already available locally — use pnpm exec to run binaries, pnpm add to install packages, pnpm dlx instead of npx.
Configuration files: Each tool uses a typed factory function for configuration:
eslint.config.ts — eslintConfig({ type: 'lib' }) or eslintConfig({ type: 'app' })
- Library projects use
type: 'lib', application projects use type: 'app'
- Framework support via flags:
eslintConfig({ astro: true }), eslintConfig({ svelte: true }), eslintConfig({ react: true })
- Frameworks are auto-detected from installed packages, but can be explicitly enabled
prettier.config.ts — prettierConfig() with optional overrides
cspell.config.ts — cspellConfig() with optional custom words
knip.config.ts — knipConfig() with optional ignore patterns
stylelint.config.js — stylelintConfig()
.remarkrc.js — remarkConfig()
tsconfig.json — extends @kitschpatrol/typescript-config
Linting commands:
pnpm ksc lint / pnpm ksc fix — Run ALL tools at once, usually mapped to the package.json "lint" and "fix" scripts.
pnpm ksc-eslint lint / pnpm ksc-eslint fix — ESLint only
pnpm ksc-prettier lint / pnpm ksc-prettier fix — Prettier only
pnpm ksc-typescript lint — TypeScript type checking only
pnpm ksc-cspell lint / pnpm ksc-cspell fix — Spell checking only (fix removes unused words from the config's words array and sorts it)
pnpm ksc-stylelint lint / pnpm ksc-stylelint fix — CSS/style linting only
pnpm ksc-remark lint / pnpm ksc-remark fix — Markdown linting only
pnpm ksc-knip lint — Unused code/export detection
Use individual commands to focus on specific linter errors instead of running the full suite.
TypeScript Configuration
Requires TypeScript 6, which enables all strict checks by default. Additional flags:
- Target:
ES2025 with DOM lib
- Module:
ESNext with bundler module resolution
noUncheckedIndexedAccess: true — indexed access on arrays and records yields T | undefined
noImplicitReturns: true — if any code path returns a value, all paths must return explicitly
noImplicitOverride: true — class members that override a base member need the override keyword
verbatimModuleSyntax: true — imports/exports used only as types must be marked with type
noFallthroughCasesInSwitch: true
erasableSyntaxOnly: true — no enum, no runtime namespace blocks, no parameter properties. Only type-level syntax that can be erased is allowed.
noUnusedLocals: true, noUnusedParameters: true
isolatedModules: true
- JSON imports enabled (
resolveJsonModule: true)
.ts extensions allowed in imports (allowImportingTsExtensions: true)