@kaliber/eslint-plugin
v2.1.2
Published
Opinionated ESLint plugin enforcing Kaliber's code conventions. Bundles custom rules, curated third-party rules, and formatting conventions into a single shareable configuration.
Maintainers
Keywords
Readme
@kaliber/eslint-plugin
Opinionated ESLint plugin enforcing Kaliber's code conventions. Bundles custom rules, curated third-party rules, and formatting conventions into a single shareable configuration.
Usage
Create an eslint.config.js file in your project root:
const kaliberConfig = require('@kaliber/eslint-plugin/eslint.config')
module.exports = [
...kaliberConfig,
// your project-specific overrides here
]Custom rules
| Rule | Description |
|---|---|
| component-properties | Destructure component props, use spread passing for same-name props, and avoid passing state setters as props |
| layout-class-name | Components are black boxes — use layoutClassName for positioning instead of className |
| naming-policy | Enforce naming conventions for components, CSS files, CSS variables, root class names, and refs |
| no-default-export | Prefer named exports over default exports — except in App, template, and page files |
| no-relative-parent-import | Disallow ../ imports — use root-slash imports that survive file moves |
| import-sort | Enforce grouped and ordered import statements with auto-fix support |
| jsx-key | Require key prop in iterators but allow keyless JSX in array-literal DSL patterns |
| position-center | Avoid place-content: center — it only aligns tracks and often does nothing |
| todo-ticket-reference | Require TODO comments to reference a Jira ticket |
Tracking rules (data-x)
| Rule | Description |
|---|---|
| data-x-required | Every <a> and <button> must have a data-x tracking attribute |
| data-x-context | Elements with data-x must also include data-x-context to identify page location |
| data-x-latin-only | data-x values must use ASCII characters only — no accented or non-Latin characters |
| data-x-clickout-prefix | External links (http/https) must use the clickout- prefix in data-x |
| data-x-cta-prefix | Call-to-action <a> elements must use the cta- prefix in data-x |
| data-x-toggle-prefix | Toggle/accordion elements must use the toggle- prefix in data-x |
| data-x-onpage-action-format | On-page actions must follow the action-target format (e.g. scroll-applyform, open-modal) |
| data-x-unique-id | Repeated elements with the same data-x must include data-x-id for disambiguation |
| data-x-sectioning-elements | Sectioning HTML elements (section, header, footer, nav, etc.) must have data-x |
| data-x-form-naming | Form elements must have a data-x value ending with -form |
Documentation
Each rule is self-contained — implementation, tests, and documentation live together:
rules/naming-policy/
index.js ← rule implementation
test.js ← rule tests
readme.md ← rule documentationThe docsUrl helper resolves the readme.md from the rule's __dirname:
Each rule exposes two pieces of metadata via meta.docs:
description— a one-liner explaining what the rule enforces. This is the primary context for both editor tooltips and AI coding assistants. LLMs that parse ESLint output see this inline without needing to follow any link.url— afile://URL pointing to the full documentation on disk. It resolves locally regardless of where the package is installed (source checkout ornode_modules), so it works without network access or GitHub authentication.
This makes rule violations self-documenting: a developer hovering over an error in VS Code sees the description and a clickable link to the full explanation. An LLM assisting with code sees the description in the lint output and can read the referenced file for deeper context on why the rule exists and how to fix violations.
Adding documentation for a new rule
- Create
rules/{rule-name}/readme.md - In the rule's
index.js, add:const docsUrl = require('../../machinery/docsUrl') module.exports = { meta: { type: 'problem', docs: { description: 'One-line description of the rule', url: docsUrl(__dirname), }, }, // ... }
Migrating from legacy config
If your project still uses a legacy .eslintrc file, you can automatically migrate to the flat config format:
Change package.json
Update the lint script in your package.json — the new flat config picks up eslint.config.js automatically, and .gitignore is used as the ignore file:
"lint.javascript": "eslint"Was:
"lint.javascript": "eslint -c .eslintrc --ignore-path .gitignore './**/*.js'"Run the migration script
# In your project directory:
npx kaliber-eslint-migrate
# Preview without writing any files:
npx kaliber-eslint-migrate --dry-run
# Print the generated config to stdout:
npx kaliber-eslint-migrate --stdout
# Overwrite an existing eslint.config.js:
npx kaliber-eslint-migrate --forceThe migration script will:
- Rename deprecated rules (e.g.
no-native-reassign→no-global-assign) - Move stylistic rules to the
@stylisticnamespace - Remap
import/rules toimport-x/ - Remove rules with no ESLint v9 replacement
- Diff your rules against the shared Kaliber config and only keep project-specific overrides
- Preserve custom globals and
ignorePatterns
Globals
Instead of depending on the globals npm package, this plugin uses an inlined machinery/globals.json containing only the environments we need (browser, node, jest).
Publishing
>> yarn publish
>> git push
>> git push --tags