Skip to content

fvena/kata

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

105 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
kata

Code quality, enforced.
Same standard for every developer, every AI agent, every commit.

npm version CI License MIT Node >=22.13.0 npm provenance npm downloads


Table of Contents

Kata is a complete code quality toolkit for Node.js and TypeScript projects. One install gives you ESLint with type-aware rules, Prettier, Stylelint, TypeScript base configs, and Markdownlint — configured to work together without conflicts.

These tools catch bugs that compile and ship to production: floating promises that silently lose data, unsafe any propagation that defeats the type system, CSS properties the browser ignores without warning, Vue ref comparisons without .value that produce conditions that are always false. Style is 5% of what they do. The other 95% is finding bugs.

If your stack includes Vue 3 or Nuxt 3, kata is the most complete opinionated setup available as a single package. If you use Node.js without a framework, the base presets cover TypeScript, JavaScript, YAML, and Markdown with the same depth.


Quick start

npm install --save-dev @franvena/kata eslint typescript
// eslint.config.js
import eslintNode from '@franvena/kata/eslint/node'
export default [...eslintNode]

That's it. Run npx eslint . to see what kata finds in your code.

Or use the interactive CLI to set up everything at once:

npx @franvena/kata init

It detects your stack, generates configs, and optionally sets up pre-commit hooks, commitlint, and CI workflows.

Need Prettier, Stylelint, or Vue/Nuxt presets? See the full setup below.


Is kata for you?

Kata is built for developers and teams who:

  • Maintain one or more Node.js/TypeScript repositories and want a single quality standard across all of them.
  • Are starting a new project and want strict linting from day one instead of bolting it on later.
  • Are migrating to ESLint v10 flat config and need a modern, maintained config that works out of the box.
  • Work with Vue 3 or Nuxt 3 and need ESLint, Stylelint, and TypeScript configured to handle .vue SFCs correctly.
  • Use AI coding tools (Copilot, Cursor, Claude) and want the same rules applied to AI-generated code as to human-written code.
  • Want type-aware ESLint rules (no-floating-promises, no-unsafe-*) without spending hours on typescript-eslint configuration.

If you only need ESLint and prefer a factory function API, @antfu/eslint-config is an excellent choice. Kata covers more ground — Prettier, Stylelint, Markdownlint, TypeScript configs — for teams that want a complete solution.


What kata catches

Kata is not about tabs vs. spaces. It catches bugs that compile, pass TypeScript, and reach production.

TypeScript & JavaScript. Floating promises that silently lose data. Unsafe any propagation that defeats the type system. Type assertions (as User) that silence the compiler until the API changes. Conditions that are always true or always false. Loops that await inside instead of using Promise.all.

CSS & SCSS. Properties the browser silently ignores (colr: red). Media queries with typos (max-widht). Shorthand properties that overwrite longhands you set three lines above. Duplicate properties with conflicting values.

Vue templates. Props mutated directly instead of emitting events. Refs compared without .value, producing conditions that are always false. Missing key attributes on v-for. Deprecated slot syntax. Accessibility violations (missing alt text, click handlers without keyboard equivalents).

Markdown. Inconsistent heading levels. Broken link references. Bare URLs without link syntax.

500+ rules across five tools. Every file type in your project covered.


Getting Started

Prerequisites

Install the peer dependencies you need:

# ESLint only
npm install --save-dev eslint typescript

# ESLint + Prettier
npm install --save-dev eslint prettier typescript

# Full stack
npm install --save-dev eslint prettier stylelint typescript

Installation

npm install --save-dev @franvena/kata

ESLint

Presets

// eslint.config.js — pick one

import eslintNode from "@franvena/kata/eslint/node";
export default [...eslintNode];

import eslintBrowser from "@franvena/kata/eslint/browser";
export default [...eslintBrowser];

import eslintVue from "@franvena/kata/eslint/vue";
export default [...eslintVue];

import eslintNuxt from "@franvena/kata/eslint/nuxt";
export default [...eslintNuxt];

Composable config blocks

Import only what you need from @franvena/kata/eslint/base:

import {
  baseIgnores,
  baseJavascript,
  baseTypeScript,
  basePerfectionist,
  baseUnicorn,
  baseComments,
  baseRegexp,
  baseYaml,
  baseMarkdown
} from '@franvena/kata/eslint/base'

export default [...baseIgnores, ...baseJavascript, ...baseTypeScript, ...basePerfectionist]
Block Description
baseIgnores Global ignore patterns
baseJavascript JS recommended + Node.js plugin
baseTypeScript Type-aware TS rules + parser
basePerfectionist Import and code organization sorting
baseUnicorn Modern JS best practices
baseRegexp Regex correctness rules
baseComments ESLint directive comment rules
baseTsdoc TSDoc validation — opt-in, see below
baseOxlint Oxlint rule disabling — opt-in, see below
baseYaml YAML linting
baseMarkdown Lint JS/TS code blocks inside Markdown

Plugins included

Plugin Purpose
@eslint/js JavaScript recommended rules
typescript-eslint Type-aware TypeScript rules + parser
eslint-plugin-n Node.js-specific rules
eslint-plugin-perfectionist Import and code organization sorting
eslint-plugin-unicorn Modern JS/TS best practices
eslint-plugin-regexp Regex correctness
eslint-plugin-tsdoc TSDoc validation (opt-in)
@eslint-community/eslint-plugin-eslint-comments ESLint directive comment rules
eslint-plugin-yml YAML linting
@eslint/markdown Lint code blocks inside Markdown

Opt-in: TSDoc validation

Only useful for libraries that publish API documentation:

import { baseTsdoc } from '@franvena/kata/eslint/base'
import eslintNode from '@franvena/kata/eslint/node'

export default [...eslintNode, ...baseTsdoc]

Opt-in: Oxlint performance layer

Oxlint runs 650+ ESLint rules in Rust at native speed. When paired with eslint-plugin-oxlint, duplicate rules are disabled in ESLint so they run only once — in Oxlint.

Install oxlint:

npm install --save-dev oxlint

Add baseOxlint to your config and prefix your lint scripts:

import { baseOxlint } from '@franvena/kata/eslint/base'
import eslintNode from '@franvena/kata/eslint/node'

export default [...eslintNode, ...baseOxlint]
{
  "scripts": {
    "lint": "oxlint && eslint .",
    "lint:fix": "oxlint --fix && eslint . --fix"
  }
}

baseOxlint is not included in any preset by default to prevent silent coverage loss for users who don't have oxlint installed. The kata init CLI configures this automatically when you select Oxlint.

Full decision: ADR-012

Vue 3

import eslintVue from '@franvena/kata/eslint/vue'
export default [...eslintVue]

Enforces on top of the base config:

  • <script setup> only — no Options API
  • Block order: <script><template><style>
  • lang="ts" on <script>, lang="scss" on <style>
  • Type-based defineProps<{}>() and defineEmits<{}>()
  • Scoped or module styles only — no global <style>
  • Shorthand directives (:prop, @event)
  • Reactive props destructuring (Vue 3.5+)
  • WCAG accessibility via eslint-plugin-vuejs-accessibility

Nuxt 3

Standalone

import eslintNuxt from '@franvena/kata/eslint/nuxt'
export default [...eslintNuxt]

With @nuxt/eslint module

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/eslint'],
  eslint: { config: { standalone: false } }
})
// eslint.config.js
import eslintNuxt from '@franvena/kata/eslint/nuxt'
import withNuxt from './.nuxt/eslint.config.mjs'

export default withNuxt([...eslintNuxt])

Opt-in: Playwright

Install the required peer dependency:

npm install --save-dev eslint-plugin-playwright

Then configure:

import eslintPlaywright from '@franvena/kata/eslint/playwright'

Opt-in: Vitest

Install the required peer dependency:

npm install --save-dev @vitest/eslint-plugin

Then configure:

import eslintVitest from '@franvena/kata/eslint/vitest'

Opt-in: Testing Library

Install the required peer dependency:

npm install --save-dev eslint-plugin-testing-library

Then configure:

import eslintTestingLibrary from '@franvena/kata/eslint/testing-library'

Opt-in: Turborepo

Install the required peer dependency:

npm install --save-dev eslint-config-turbo

Then configure:

import eslintTurbo from '@franvena/kata/eslint/turbo'

Prettier

// prettier.config.js
export { default } from '@franvena/kata/prettier'
// extend
import prettierConfig from '@franvena/kata/prettier'
export default { ...prettierConfig }

Why Prettier is not an ESLint plugin: ESLint owns code quality, Prettier owns formatting — they don't overlap. eslint-config-prettier disables all formatting rules so there are zero conflicts and no duplicate work in the IDE.


Stylelint

// stylelint.config.js
export default {
  extends: ['@franvena/kata/stylelint']
}

Includes stylelint-config-standard-scss and stylelint-config-recess-order. Vue SFCs are supported via postcss-html custom syntax (configured automatically).


TypeScript

{ "extends": "@franvena/kata/typescript/node" }
{ "extends": "@franvena/kata/typescript/browser" }

Strict mode, noUncheckedIndexedAccess, modern module settings. Based on Matt Pocock's TSConfig Cheat Sheet.


Markdown

{ "extends": "@franvena/kata/markdown" }

Requires the markdownlint VS Code extension.


Customizing kata

Kata is opinionated by default and flexible when you need it. Every rule can be overridden, every block can be replaced, and every tool is optional.

Override a rule

// eslint.config.js
import eslintNode from '@franvena/kata/eslint/node'

export default [
  ...eslintNode,
  {
    rules: {
      // Disable a rule entirely
      'unicorn/no-array-for-each': 'off',
      // Change severity from error to warning
      '@typescript-eslint/no-explicit-any': 'warn'
    }
  }
]

Compose only the blocks you need

Every preset is built from composable blocks. Import them individually to build a custom config:

import {
  baseIgnores,
  baseJavascript,
  baseTypeScript,
  baseUnicorn
} from '@franvena/kata/eslint/base'

export default [...baseIgnores, ...baseJavascript, ...baseTypeScript, ...baseUnicorn]

See the full list of available blocks in the ESLint section.

Use strict TypeScript checking

The default TypeScript preset uses recommendedTypeChecked — the rules with the highest signal-to-noise ratio. For teams comfortable with stricter checking, switch to strictTypeChecked:

import eslintNode from '@franvena/kata/eslint/node'
import tseslint from 'typescript-eslint'

export default [...eslintNode, ...tseslint.configs.strictTypeChecked]

strictTypeChecked adds rules like no-unnecessary-condition, no-confusing-void-expression, and stricter restrict-template-expressions. It catches more edge cases but may produce false positives in idiomatic code. It is also not semver-stable in typescript-eslint — rules can change in minor versions.

Relax Vue SFC enforcement during migration

If you're adopting kata on an existing Vue project, some rules may flag every component. Relax them temporarily:

import eslintVue from '@franvena/kata/eslint/vue'

export default [
  ...eslintVue,
  {
    rules: {
      // Allow plain CSS while migrating to SCSS
      'vue/block-lang': 'warn',
      // Allow global styles temporarily
      'vue/enforce-style-attribute': 'warn'
    }
  }
]

Different presets per package in a monorepo

Each package in a monorepo can use a different kata preset. Each needs its own eslint.config.js and tsconfig.json:

packages/
  api/
    eslint.config.js    → imports @franvena/kata/eslint/node
    tsconfig.json       → extends @franvena/kata/typescript/node
  web/
    eslint.config.js    → imports @franvena/kata/eslint/vue
    tsconfig.json       → extends @franvena/kata/typescript/browser

Run ESLint from the root with the --config flag or from each package directory.


Migrating to kata

Integration guides:

ESLint v10 removed the legacy .eslintrc format. If your current config uses .eslintrc.js, .eslintrc.json, or .eslintrc.yml, it will not work with ESLint v10. All migration guides above assume ESLint v10 flat config.


Comparison with alternatives

kata @antfu/eslint-config XO Biome Manual setup
ESLint flat config Yes Yes Yes N/A Yes
TypeScript type-aware rules Yes Yes Yes Partial Yes
Prettier Integrated No (uses Stylistic) Optional Built-in formatter Manual
Stylelint (CSS/SCSS/Vue) Integrated No No No Manual
Markdownlint Integrated No No No Manual
TypeScript base configs Integrated No No No Manual
Vue 3 / Nuxt 3 presets Yes Yes No Experimental Manual
Composable config blocks Yes Factory function CLI wrapper Config file N/A
Single package install Yes Yes Yes Yes 5-12 packages

@antfu/eslint-config is excellent if you only need ESLint and prefer ESLint Stylistic over Prettier. It auto-detects Vue and TypeScript, and Anthony Fu (Vue/Nuxt core team) maintains it actively. Choose antfu if ESLint is all you need. Choose kata if you also need Prettier, Stylelint, Markdownlint, and TypeScript base configs in one package.

XO wraps ESLint in a CLI with strong defaults. It does not cover CSS, Markdown, or Vue-specific rules. Good for Node.js-only projects that want zero config.

Biome is 25x faster and unifies linting and formatting in a single tool. However, Vue SFC support is experimental, SCSS is not supported, Markdown linting has no active development, and type-aware rules (the ones that catch the most bugs) are not available. For Vue/Nuxt projects with TypeScript and SCSS, the coverage gap is significant. See ADR-009 for the full evaluation.

Manual setup gives you full control. But integrating ESLint, Prettier, Stylelint, TypeScript, and Markdownlint without conflicts takes hours — and you repeat that work in every repository. Kata absorbs the integration and keeps it updated.


Compatibility

Tool Supported versions
Node.js >=22.13.0
ESLint ^10.0.0
TypeScript ^5.7.0
Vue ^3.4.0
Nuxt ^3.10.0
Prettier ^3.0.0
Stylelint ^17.0.0

Troubleshooting

projectService errors: cannot find tsconfig

Symptom: Error: No tsconfig.json found for file X.

Fix — root config files (eslint.config.js, vite.config.ts): add a tsconfig.node.json that covers them:

{
  "extends": "@franvena/kata/typescript/node",
  "include": ["*.config.ts", "*.config.js"]
}

Fix — monorepo: each package needs its own tsconfig.json with an include that covers all linted files.


vue/block-lang errors on an existing project

Symptom: Every .vue file errors with Expected "scss" as the lang of the "style" block.

Fix: Relax while migrating, then tighten once complete:

export default [...eslintVue, { rules: { 'vue/block-lang': 'warn' } }]

Conflicts with @nuxt/eslint module

Fix: Set standalone: false in nuxt.config.ts. See Nuxt 3 with @nuxt/eslint module.


Accessibility false positives

Fix: Disable the rule if it has no configuration options that fit your case:

export default [
  ...eslintVue,
  { rules: { 'vuejs-accessibility/click-events-have-key-events': 'off' } }
]

Design Philosophy

These are the deliberate decisions behind this config — not just what the rules do, but why.

Flat config over legacy .eslintrc — ESLint 9's flat config is the only supported format. Native JavaScript composition via array spread, no implicit merging, no hidden config resolution. Every rule is traceable. → Full decision: ADR-001

recommendedTypeChecked as the default — The TypeScript-ESLint preset with the highest signal-to-noise ratio. Catches floating promises, unsafe any propagation, and misused promises — the rules that prevent the most real bugs. strictTypeChecked is available as an opt-in for teams that want additional strictness (see Customizing kata). The default was changed from strictTypeChecked because it is not semver-stable in typescript-eslint — rule changes can occur in minor versions, which would break user builds without kata publishing a new version. → Full decision: ADR-002

projectService over project: [...] — Auto-discovers tsconfig.json files relative to each linted file. Handles monorepos, nested packages, and workspace setups without manual path arrays. Tradeoff: tsconfigRootDir points to the package root, not the consumer's project — intentional, documented in source. → Full decision: ADR-003

Prettier is not an ESLint plugin — ESLint runs on every keystroke; formatting errors add noise. eslint-config-prettier disables all formatting rules from every plugin so there are zero conflicts. Single responsibility: ESLint owns correctness, Prettier owns style. → Full decision: ADR-004

Vue SFCs enforce TypeScript and SCSSvue/block-lang requires lang="ts" and lang="scss". Mixed-language SFCs create inconsistency that accumulates into maintenance debt. The default is strict; override explicitly if you need flexibility. → Full decision: ADR-005

Scoped or module styles onlyvue/enforce-style-attribute with ["scoped", "module"]. Global <style> blocks are the most common source of CSS leakage in Vue apps. If you need global styles, use a standalone .scss file. → Full decision: ADR-006

Composable blocks as the public API — Named exports (baseIgnores, baseTypeScript, etc.) instead of a single opaque object. Each preset is composed from these blocks — no hidden configuration. Consumers can audit the full rule set by reading the import chain, and build custom presets without forking. → Full decision: ADR-007

Node.js 22 as the minimum — Requires Node.js >=22.13.0 for stable ESM support, import.meta.dirname, and the native test runner. → Full decision: ADR-008

ESLint + Prettier as the base — Evaluated Biome and Oxlint as alternatives. Biome lacks Vue SFC support, SCSS linting, and type-aware rules. ESLint + Prettier remains the most complete option for Vue/Nuxt projects. → Full decision: ADR-009

ESLint v10 feature adoption — Evaluated defineConfig (already adopted), multithreading, bulk suppressions, and official CSS/HTML plugins. Stylelint remains necessary for SCSS and Vue scoped styles. → Full decision: ADR-010

Stylelint standard configs and Vue via postcss-html — Replaced stylelint-config-recommended-vue (incompatible with Stylelint 17) with postcss-html custom syntax. Upgraded to stylelint-config-standard-scss for stricter CSS conventions. → Full decision: ADR-011

Oxlint as opt-in performance layer — Oxlint runs 650+ ESLint rules in Rust. Exposed as baseOxlint composable block (not in default export) to prevent silent coverage loss. CLI handles opt-in configuration. → Full decision: ADR-012


Scripts

Recommended scripts for a typical project. The kata init CLI generates these automatically.

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "lint:css": "stylelint 'src/**/*.{scss,css,vue}'",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "typecheck": "tsc --noEmit"
  }
}

Examples

Working examples for every supported stack, each tested in CI:

Project Stack Preset
node-typescript Node.js + TypeScript eslint/node
node-typescript-browser Node.js + TypeScript (browser) eslint/browser
node-javascript Node.js + JavaScript eslint/base (composable)
vue-typescript Vue 3 + TypeScript + SCSS eslint/vue
nuxt-typescript Nuxt 3 + TypeScript + SCSS eslint/nuxt
monorepo API (Node) + Web (Vue) Mixed presets

Each example includes valid.* files (should pass lint) and invalid.* files (should produce errors). They use "@franvena/kata": "file:../../" to link against the local version.


Versioning

This project follows Semantic Versioning. As a linting config, rule changes follow this contract:

Patch (1.0.x): Bug fixes to existing rules. No new diagnostics in your code.

Minor (1.x.0): New rules that produce warnings only. New opt-in configs (e.g., a new eslint/react preset). Documentation improvements. Dependency updates that don't change lint behavior.

Major (x.0.0): New rules that produce errors. Existing rules becoming stricter (warning → error, or new cases flagged). Adding or removing plugins from core presets. Dropping Node.js versions. Changes to the export map or public API.

Upgrade guarantee

When you run npm update @franvena/kata within the same major version, your build will not break due to new lint errors. Warnings may appear — errors will not.

Every major version includes a migration guide documenting what changed, why, and how to resolve new errors.


What's next

  • eslint-plugin-import-x — Waiting for v5 with ESLint 10 support. Target rules: no-cycle, no-self-import, no-extraneous-dependencies, no-duplicates.
  • Biome watch — Biome doesn't support Vue SFCs, SCSS, or Markdown at production quality yet. When it does, kata will evaluate migration. The export map architecture allows transparent tooling changes. See ADR-009.
  • ESLint multithreaded linting — ESLint v10 supports --concurrency=auto for 1.3x-3x speedups. kata configs are compatible. See ADR-010.

Contributing

See CONTRIBUTING.md for setup, conventions, and release process.


License

MIT © Francisco Vena

About

A shared configuration for ESLint, Prettier, and Stylelint to ensure consistent and clean code across your projects.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors