Skip to content

Configuration

Fred Souza edited this page Aug 3, 2026 · 1 revision

lanekeep.json, at the project root. lanekeep init writes one matched to your project.

{
  "$schema": "https://raw.githubusercontent.com/fmsouza/lanekeep/main/schema/lanekeep.schema.json",

  "include": ["**/*.go"],
  "exclude": ["**/*_test.go"],

  "rules": [
    "lanekeep/no-package-init",
    { "rule": "lanekeep/no-restricted-imports", "options": { "restrictions": [] } },
    "./lanekeep/rules/no-fmt-println.ts"
  ]
}

Keep the $schema line. It is what gives you completion and validation in your editor with nothing installed — VS Code and most others read it directly.

Fields

Field Type Meaning
include string[] Globs selecting files to check, relative to the project root
exclude string[] Globs removing files from that selection, applied after include
rules (string | object)[] The rules to run, in order
namespaces string[] Rule-id namespaces beyond local
severity object Override a rule's own severity, by id
timeouts object rule and global budgets, in milliseconds

rules

A string uses a rule as it comes:

"lanekeep/no-package-init"
"./lanekeep/rules/mine.ts"

An object calls it with options, which is what a rule taking configuration expects:

{ "rule": "lanekeep/no-restricted-imports", "options": { "restrictions": [
  { "module": "database/sql", "from": ["!internal/store/**"], "reason": "go through the store package" }
] } }

Built-ins are lanekeep/<name>. Your own rules are paths, starting ./.

namespaces

Rule ids are namespaced. lanekeep/ is reserved for built-ins and local/ needs no declaration; any other prefix must be listed here:

{ "namespaces": ["acme"], "rules": ["./rules/acme-no-raw-sql.ts"] }

A rule whose namespace is not declared is rejected. That is deliberate: a typo in an id would otherwise be a rule that silently never runs, which looks exactly like passing.

severity

{ "severity": { "lanekeep/no-default-export": "warn" } }

error, warn or off. Useful for adopting a rule gradually without editing it.

timeouts

{ "timeouts": { "rule": 500, "global": 15000 } }

Breaching either cancels the run and exits 2 rather than reporting a partial result as a clean one. A rule that hangs a pre-commit hook is indistinguishable from a broken tool.

Configuring in TypeScript instead

lanekeep.config.ts still works, and is the better choice when the config computes something or shares a preset across repositories — composition is then ordinary import, with no bespoke extends mechanism to learn.

import { defineConfig } from 'lanekeep'
import noDefaultExport from 'lanekeep/no-default-export'
import noDebugger from './lanekeep/rules/no-debugger'

export default defineConfig({
  include: ['src/**/*.{ts,tsx}'],
  rules: [noDefaultExport, noDebugger],
})

Both formats compile to the same thing before anything reads them, so they cannot differ in behavior. lanekeep.json wins if a project has both.

Suppressions

// lanekeep-ignore-next-line lanekeep/no-default-export reason: legacy entry point
export default parse

The reason is mandatory. An optional expiry is supported. A directive that does not work — a missing reason, a bare rule id, an unreadable date — is reported rather than silently doing nothing.

lanekeep check --report-unused-suppressions

finds the ones that no longer silence anything, which is otherwise a decision about code that has since changed and nothing will ever tell you.

Clone this wiki locally