# Configuration

For an overview of all available linters, go [here](linters.md).

Table of contents:

<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
**Table of Contents**

- [Configuration](#configuration)
    - [Introduction](#introduction)
    - [Namespace local configuration](#namespace-local-configuration)
        - [:config-in-ns](#config-in-ns)
        - [Metadata config](#metadata-config)
    - [Exclude files from being linted](#exclude-files-from-being-linted)
    - [Unrecognized macros](#unrecognized-macros)
        - [Inline macro configuration](#inline-macro-configuration)
    - [Options](#options)
        - [Disable a linter](#disable-a-linter)
        - [Enable optional linters](#enable-optional-linters)
        - [Disable all linters but one](#disable-all-linters-but-one)
        - [Ignore warnings in an expression](#ignore-warnings-in-an-expression)
        - [Lint a custom macro like a built-in macro](#lint-a-custom-macro-like-a-built-in-macro)
        - [Override config in comment forms](#override-config-in-comment-forms)
        - [:config-in-call](#config-in-call)
        - [:config-in-tag](#config-in-tag)
        - [Ignore the contents of comment forms](#ignore-the-contents-of-comment-forms)
        - [Disable auto-load-configs](#disable-auto-load-configs)
        - [Only lint specific dialects in .cljc files](#only-lint-specific-dialects-in-cljc-files)
    - [Available linters](#available-linters)
    - [Hooks](#hooks)
    - [Output](#output)
        - [Print results in JSON format](#print-results-in-json-format)
        - [Print results in SARIF format](#print-results-in-sarif-format)
        - [Print results with a custom format](#print-results-with-a-custom-format)
        - [Include and exclude files from the output](#include-and-exclude-files-from-the-output)
        - [Show progress bar while linting](#show-progress-bar-while-linting)
        - [Output canonical file paths](#output-canonical-file-paths)
        - [Show linter name in message](#show-linter-name-in-message)
        - [Show language context in .cljc files](#show-language-context-in-cljc-files)
    - [Namespace groups](#namespace-groups)
    - [Example configurations](#example-configurations)
    - [Exporting and importing configuration](#exporting-and-importing-configuration)
        - [Exporting](#exporting)
        - [Sample Exports](#sample-exports)
        - [Importing](#importing)
    - [Deprecations](#deprecations)

<!-- markdown-toc end -->

## Introduction

Clj-kondo can be configured in several ways:

- home dir config in `~/.config/clj-kondo/config.edn` (respects `XDG_CONFIG_HOME`).
- project config: a `config.edn` file in the `.clj-kondo` directory (see
  [project setup](../README.md#project-setup)).
- `:config-paths` in project `config.edn`: a list of directories that provide additional config.
- command line `--config` file or EDN arguments.
- namespace local config using `:clj-kondo/config` metadata in the namespace form (see below).

The configurations are merged in the following order, where a later config overrides an earlier config:

- home dir config
- `:config-paths` in project config
- project config
- `CLJ_KONDO_EXTRA_CONFIG_DIR` environment variable
- command line config
- namespace local config

The `^:replace` metadata hint can be used to replace parts or all of the
configuration instead of merging with previous ones. The home dir config is
implicitly part of `:config-paths`. To opt out of merging with home dir config
use `:config-paths ^:replace []` in your project config.

Look at the [default configuration](../src/clj_kondo/impl/config.clj) for all
available options.

## Namespace local configuration

Clj-kondo supports configuration on the namespace level, in two ways.

### :config-in-ns

The `:config-in-ns` option can be used to change the configuration while linting
a specific namespace.

``` clojure
{:config-in-ns {my.namespace {:linters {:unresolved-symbol {:level :off}}}}}
```

This will silence unresolved symbol errors in the following:

``` clojure
(ns my.namespace)
x y z
```

See [Namespace groups](#namespace-groups) on how to configure multiple namespace
in one go.

Since clj-kondo 2023.03.17 you can use a shorter notation to disable multiple linters inside of a namespace:

``` clojure
{:config-in-ns {my.namespace {:ignore [:unresolved-namespace]}}}
```

to suppress specific linters or

``` clojure
{:config-in-ns {my.namespace {:ignore true}}}
```

to suppress all linters inside of that namespace.

### Metadata config

Clj-kondo supports config changes while linting a namespace via namespace
metadata. The same example as above, but via metadata:

``` clojure
(ns my.namespace
  {:clj-kondo/config '{:linters {:unresolved-symbol {:level :off}}}})
x y z
```
Note that namespace local config must always be quoted on the outside:

`{:clj-kondo/config '{:linters ...}}`

Quotes should not appear inside the config.

Since clj-kondo 2023.03.17, you can use a shorter notation to suppress linters in namespace:

`{:clj-kondo/ignore [:unresolved-symbol]}`

to suppress certain linters within a namespace or simply `{:clj-kondo/ignore
true}` to suppress them all.

## Exclude files from being linted

You can prevent files from being linted, using the following configuration:

``` clojure
{:exclude-files "my_regex.cljc$"}
```

Note that the string for the `:exclude-files` represents a regex which will be
executing through `re-find` on the filename. To improve cross-platform behavior
you must use slashes on all platforms including Windows as the file separator:

``` Clojure
{:exclude-files "src/my_regex.cljc$"}
```

## Unrecognized macros

Clj-kondo only expands a selected set of macros from clojure.core and some
popular community libraries. For unrecognized macros you can use these
configurations:

- [`:lint-as`](#lint-a-custom-macro-like-a-built-in-macro)
- [`:unresolved-symbol`](./linters.md#unresolved-symbol)
- [`:hooks`](#hooks)

### Inline macro configuration

Since clj-kondo 2023.03.17 you can use metadata on the macro as a way to configure the behavior of macro _calls_:

``` Clojure
(defmacro with-foo
  {:clj-kondo/lint-as 'clojure.core/let}
  [bnds & body]
  `(let [~@bnds]
     ~@body))

(with-foo [a 1]
  a) ;; no warning, macro configured as clojure.core/let

(defmacro matcher
  {:clj-kondo/ignore [:unresolved-symbol :type-mismatch]}
  [m match-expr & body]
  ;; dummy
  [m match-expr body])

(matcher {:a 1} {?a 1} (inc :foo)) ;; no warning in usage of macro
```

Additionally `:clj-kondo/config` key is supported where you can supply a
configuration that will be applied within macro calls.

The metadata configuration on macros is automatically exported to
`.clj-kondo/inline-configs/macro_namespace.clj/config.edn` when linting the
file.  Note that the file containing the macro needs to be linted first and only
after that, the right configuration will be in place such that macro usages will
be correctly linted. This will happen when you follow the [project setup
instructions](https://github.com/clj-kondo/clj-kondo?tab=readme-ov-file#project-setup).
Note that inline configurations can also be configured using `:config-in-call`
in the `.clj-kondo/config.edn` file.

## Options

This section covers general configuration options. For an overview of all
available linters, go [here](linters.md).

### Disable a linter

``` shellsession
$ echo '(select-keys [:a])' | clj-kondo --lint -
<stdin>:1:1: error: wrong number of args (1) passed to clojure.core/select-keys
linting took 10ms, errors: 1, warnings: 0

$ echo '(select-keys [:a])' | clj-kondo --lint - --config '{:linters {:invalid-arity {:level :off}}}'
linting took 10ms, errors: 0, warnings: 0
```

Since clj-kondo 2023.03.17 a shorter notation is supported:

``` Clojure
{:ignore [:unresolved-symbol]}
```

to suppress one or more linters, or:

``` Clojure
{:ignore true}
```

to suppress all linters.

See also:

- [:config-in-ns](#config-in-ns)
- [:config-in-call](#config-in-call)
- [:config-in-tag](#config-in-tag)
- [ignore warnings in an expression](#ignore-warnings-in-an-expression)

for fine-grained configuration.

### Enable optional linters

Some linters are not enabled by default. Right now these linters are:

- `:missing-docstring`: warn when public var doesn't have a docstring.
- `:unsorted-required-namespaces`: warn when namespaces in `:require` are not sorted.
- `:refer`: warn when there is **any** usage of `:refer` in your namespace requires.
- `:single-key-in`: warn when using assoc-in, update-in or get-in with single key.
- `:shadowed-var`: warn when a binding shadows a var.
- `:docstring-no-summary`: warn when first line of docstring is not a complete sentence.
- `:docstring-leading-trailing-whitespace`: warn when docstring has leading or trailing whitespace.
- `:used-underscored-binding`: warn when a underscored (ie marked as unused) binding is used.
- `:warn-on-reflection`: warns about not setting `*warn-on-reflection*` to true in Clojure
namespaces.
- `:unused-value`: warns about unused values.
- `:redundant-call`: warns about redundant calls, mostly macros and functions that return their first argument as a no-op
- `:redundant-str-call`: warn about unnecessary `str` calls

You can enable these linters by setting the `:level`:

``` clojure
{:linters {:missing-docstring {:level :warning}}}
```

### Disable all linters but one

You can accomplish this by using `^:replace` metadata, which will override
instead of merge with other configurations:

``` shellsession
$ clj-kondo --lint corpus --config '^:replace {:linters {:redundant-let {:level :info}}}'
corpus/redundant_let.clj:4:3: info: redundant let
corpus/redundant_let.clj:8:3: info: redundant let
corpus/redundant_let.clj:12:3: info: redundant let
```

### Ignore warnings in an expression

To ignore all warnings in an expression, place a hint before it. It uses reader
comments, so they won't end up in your runtime.

``` clojure
#_:clj-kondo/ignore
(inc 1 2 3)
```

To ignore warnings from just one or a few linters:

``` clojure
#_{:clj-kondo/ignore [:invalid-arity]}
(inc 1 2 3)
```

To ignore warnings for only one language in a reader conditional:

``` clojure
#_{:clj-kondo/ignore #?(:clj [:unused-binding] :cljs [])}
(defn foo [x]
  #?(:cljs x)) ;; x is only used in cljs, but unused is ignored for clj, so no warning
```

### Lint a custom macro like a built-in macro

In the following code the `my-defn` macro is defined, but clj-kondo doesn't know how to interpret it:

``` clojure
(ns foo)

(defmacro my-defn [name args & body]
  `(defn ~name ~args
     (do (println "hello!")
       ~@body)))

(my-defn foo [x])
```

Hence `(foo 1 2 3)` will not lead to an invalid arity error. However, the syntax
of `my-defn` is a subset of `clojure.core/defn`, so for detecting arity errors
we might have just linted it like that. That is what the following configuration accomplishes:

``` clojure
{:lint-as {foo/my-defn clojure.core/defn}}
```

When you have custom `def` or `defn`-like macros and you can't find a supported macro that is like it, you can use:

``` clojure
{:lint-as {foo/my-defn clj-kondo.lint-as/def-catch-all}}
```

### Override config in comment forms

```clojure
{:config-in-comment {:linters {:unresolved-namespace {:level :off}}}}
```

### :config-in-call

The `config-in-call` configuration option lets you tweak configuration within calls.

E.g. given this macro:

``` Clojure
(ns my-ns)

(defmacro ignore [& _body])

(ignore x y z)
```

you can ignore unresolved symbols using:

``` Clojure
{:config-in-call {my-ns/ignore {:linters {:unresolved-symbol {:level :off}}}}}
```

Since clj-kondo 2023.03.17 a shorter notation is supported to suppress linters:

``` Clojure
{:config-in-call {my-ns/ignore {:ignore [:unresolved-symbol]}}}
```

to suppress a specific linter or:

``` Clojure
{:config-in-call {my-ns/ignore {:ignore true}}}
```

to suppress all warnings.

### :config-in-tag

The `config-in-tag` configuration option lets you tweak configuration within
reader tags. By default, unresolved symbols and invalid arity calls are ignored
in reader tags since they can do arbitrary transformations at read time. E.g. this will not give an error:

``` Clojure
#jsx [:foo {:href x}]
```

To get unresolved symbol warnings back, use:

``` Clojure
{:config-in-tag {jsx {:linters {:unresolved-symbol {:level :error}}}}}
```

### Ignore the contents of comment forms

If you prefer not to lint the contents of `(comment ...)` forms, use this configuration:

```clojure
{:skip-comments true}
```

### Disable auto-load-configs

By default configurations in `.clj-kondo/*/*/config.edn` are used. You can disable this with:

``` shellsession
:auto-load-configs false
```

### Only lint specific dialects in .cljc files

By default, clj-kondo will lint all dialects it understands in .cljc files. To focus on
a specific dialect or subset of dialects, use `{:cljc {:features [LANG*]}}`, where LANG
is a given dialect keyword.

Given this example `example.cljc` file:
```clojure
(+ 5 #?(:clj "foo" :cljs "bar"))
```

With `{:cljc {:features [:cljs]}}`, only the issues in the `clojurescript` branch will be linted:

```clojure
$ clj-kondo --lint /tmp/example.cljc
/tmp/example.cljc:1:14: warning: Expected: number, received: string.
/tmp/example.cljc:1:26: warning: Expected: number, received: keyword.
linting took 16ms, errors: 0, warnings: 2

$ clj-kondo --lint /tmp/example.cljc --config '{:cljc {:features [:cljs]}}'
/tmp/example.cljc:1:26: warning: Expected: number, received: keyword.
linting took 11ms, errors: 0, warnings: 1
```

## Available linters

See [linters.md](linters.md).

## Hooks

See [hooks.md](hooks.md).

## Output

### Print results in JSON format


``` shell
$ clj-kondo --lint corpus --config '{:output {:format :json}}' | jq '.findings[0]'
{
  "type": "invalid-arity",
  "filename": "corpus/nested_namespaced_maps.clj",
  "row": 9,
  "col": 1,
  "level": "error",
  "message": "wrong number of args (2) passed to nested-namespaced-maps/test-fn"
}
```

Printing in EDN format is also supported.

### Print results in SARIF format

Use `--config '{:output {:format :sarif}}`

### Print results with a custom format

``` shell
$ clj-kondo --lint corpus --config '{:output {:pattern "::{{level}} file={{filename}},line={{row}},col={{col}}::{{message}}"}}'
::warning file=corpus/compojure/core.clj,line=2,col=19::Unsorted namespace: foo
```

The custom pattern supports these template values:

| Template Variable | Notes                                                     |
|-------------------|-----------------------------------------------------------|
| `{{filename}}`    | File name                                                 |
| `{{row}}`         | Row where linter violation starts                         |
| `{{end-row}}`     | Row where linter violation ends                           |
| `{{col}}`         | Column where linter violation starts                      |
| `{{end-col}}`     | Column where linter violation ends                        |
| `{{level}}`       | Lowercase level of linter warning, one of info,warn,error |
| `{{LEVEL}}`       | Uppercase variant of `{{level}}`                          |
| `{{message}}`     | Linter message                                            |
| `{{type}}`        | Linter violation type                                     |

### Include and exclude files from the output

``` shellsession
$ clj-kondo --lint "$(clj -Spath)" --config '{:output {:include-files ["^clojure/test"]}}'
clojure/test.clj:496:6: warning: redundant let
clojure/test/tap.clj:86:5: warning: redundant do
linting took 3289ms, errors: 0, warnings: 2

$ clj-kondo --lint "$(clj -Spath)" --config '{:output {:include-files ["^clojure/test"] :exclude-files ["tap"]}}'
clojure/test.clj:496:6: warning: redundant let
linting took 3226ms, errors: 0, warnings: 1
```

### Show progress bar while linting

``` shellsession
$ clj-kondo --lint "$(clj -Spath)" --config '{:output {:progress true}}'
.................................................................................................................
cljs/tools/reader.cljs:527:9: warning: redundant do
(rest of the output omitted)
```

### Output canonical file paths

The config `'{:output {:canonical-paths true}}'` will output canonical file
paths (absolute file paths without `..`). This also shows the full path of a jar
file when you lint a classpath.

``` shellsession
$ clj-kondo --lint corpus --config '{:output {:canonical-paths true}}'
/Users/borkdude/dev/clj-kondo/corpus/cljc/datascript.cljc:8:1: error: datascript.db/seqable? is called with 2 args but expects 1
(rest of the output omitted)
```

### Show linter name in message

Adding `'{:output {:linter-name true}}` will append rule name to the output line for each reported finding.

By default, this configuration is set to `false`.

Output example with default `false`:

```shell
$ echo '(def x (def x 1))' | clj-kondo --lint -
<stdin>:1:1: warning: redefined var #'user/x
<stdin>:1:8: warning: inline def
linting took 22ms, errors: 0, warnings: 2
```

Output example with `{:output {:linter-name true}}`:

```shell
$ echo '(def x (def x 1))' | clj-kondo --config '{:output {:linter-name true}}' --lint -
<stdin>:1:1: warning: redefined var #'user/x [:redefined-var]
<stdin>:1:8: warning: inline def [:inline-def]
linting took 9ms, errors: 0, warnings: 2
```

### Show language context in .cljc files

Given this example `example.cljc` file:
``` Clojure
x

(let #?(:clj [x 1]
        :cljs [])
  )
```

When setting `{:output {:langs true}}` clj-kondo will output the language context for each error in the .cljc file:

``` Clojure
$ clj-kondo --lint /tmp/example.cljc --config '{:output {:langs true}}'
/tmp/example.cljc:1:1: error: Unresolved symbol: x [clj, cljs]
/tmp/example.cljc:3:15: warning: unused binding x [clj]
linting took 14ms, errors: 1, warnings: 1
```

## Namespace groups

Sometimes it is desirable to configure a group of namespaces in one go. This can be done by creating namespace groups:

``` clojure
{:ns-groups [{:pattern "foo\\..*" :name foo-group}
             {:filename-pattern ".*bar_baz.*" :name bar-baz-group}]}
```

Each group consists of a `:name` and either a `:pattern` which matches the namespace, or a `:filename-pattern` which matches the filename.
The pattern is evaluated by `re-pattern` and matched with `re-find`.

Namespace groups can be used in the following configurations:

- In the `:discouraged-var` linter: `{foo-group/some-var {:message "..."}}`
- In the `:discouraged-namespace` linter: `{foo-group {:message "..."}}`
- In `:config-in-ns`: `{foo-group {:linters {:unresolved-symbol {:level :off}}}}`
- In the `:unresolved-namespace` linter
- In `:hooks`: `:analyze-call` and `:macroexpand`

Namespace groups can be extended to more linters. Please make an issue to request this.

## Example configurations

These are some example configurations used in real projects. Feel free to create a PR with yours too.

- [clj-kondo](https://github.com/clj-kondo/clj-kondo/blob/master/.clj-kondo/config.edn)
- [rewrite-cljc](https://github.com/lread/rewrite-cljs-playground/blob/master/.clj-kondo/config.edn)

Also see the [config](https://github.com/clj-kondo/config) project.

## Exporting and importing configuration

### Exporting

A library will often have clj-kondo config that is useful to its users.
This config might include such things as custom [hooks](hooks.md) and `:lint-as` rules for the library's public API.

You specify a clj-kondo config export for your library in a `config.edn` file under the following directory structure:

``` shellsession
clj-kondo.exports/<your-org>/<your-libname>/
```

This directory structure must be on your library's classpath.
For a jar file, this means including it off the root of within the jar.

When a user includes your library as a dependency for their project, clj-kondo, when asked, will automatically import your library's clj-kondo export config to the user's project `.clj-kondo` directory.
The user will then activate your config at their discretion.
See [importing](#importing) for details.

Remember that your exported clj-kondo config is to help users of your library lint against your library.
An exported config will, in most cases, be different from your local `.clj-kondo/config.edn`.
The local config will typically also contain personal preferences and internal lint rules.

To activate the exported configuration in your local project, you can add the following to `.clj-kondo/config.edn`:

``` Clojure
{:config-paths ["../resources/clj-kondo.exports/<your-org>/<your-libname>"]}
```

### Sample Exports

The clj-kondo team has provided config exports for some popular libraries in the [clj-kondo/config](https://github.com/clj-kondo/config) repo.
Let's take a look at its clj-kondo exports:

```shellsession:
❯ tree -F resources
resources
└── clj-kondo.exports/
    └── clj-kondo/
        ├── claypoole/
        │   ├── clj_kondo/
        │   │   └── claypoole.clj
        │   └── config.edn
        ├── fulcro/
        │   └── config.edn
        ├── mockery/
        │   ├── clj_kondo/
        │   │   └── mockery/
        │   │       ├── with_mock.clj
        │   │       └── with_mocks.clj
        │   └── config.edn
        ├── rum/
        │   ├── clj_kondo/
        │   │   └── rum.clj
        │   └── config.edn
        └── slingshot/
            ├── clj_kondo/
            │   └── slingshot/
            │       └── try_plus.clj
            └── config.edn
```

The clj-kondo/config repo:

- Includes a `config.edn` for each library. This defines the clj-kondo export config.
- Includes custom [hooks](hooks.md) for some libraries under `clj_kondo`.
- Includes `"resources"` in its `deps.edn` `:paths` so that the exports will be included on the classpath when the repo is included as a `:git/url` dependency.
- Uses `clj-kondo` as the org name to not conflict with configurations that the owners of these libraries might themselves choose to export.
For example, if the `claypoole` library itself wanted to export config, it would use its proper org-name instead of `clj-kondo`:
    ```shellsession
    clj-kondo.exports
    └── com.climate
        └── claypoole
    ```

### Importing

Clj-kondo, when asked, will copy clj-kondo configs found in library dependencies.
As an example, let's add [clj-kondo/config](#sample-exports) as a dependency.

1. Include `clj-kondo/config` in your `deps.edn`:
    ```Clojure
    {:deps {clj-kondo/config {:git/url "https://github.com/clj-kondo/config"
                              :sha "c37c13ea09b6aaf23db3a7a9a0574f422bb0b4c2"}}}
    ```
2. Ensure a `.clj-kondo` directory exists, if necessary:
    ```
    $ mkdir .clj-kondo
    ```
3. Then ask clj-kondo to copy configs like so:
    ```
    $ clj-kondo --lint "$(clojure -Spath)" --copy-configs --skip-lint
    Configs copied:
    - .clj-kondo/clj-kondo/better-cond
    - .clj-kondo/clj-kondo/claypoole
    - .clj-kondo/clj-kondo/mockery
    - .clj-kondo/clj-kondo/rum
    - .clj-kondo/clj-kondo/slingshot
    ```
4. Now enrichen clj-kondo's linting cache via:
    ```
    $ clj-kondo --lint $(clojure -Spath) --dependencies --parallel
    ```
4. That's it, your linting experience for your library dependencies is now augmented.
You can now lint your project as normal, for example:
    ```
    $ clj-kondo --lint src:test
    ```
Clj-kondo configurations are only copied when both of these requirements are met:

- There is a `.clj-kondo` directory in your project.
This directory is where clj-kondo will copy configs.
- The `--copy-configs` flag is present.
This tells clj-kondo to copy clj-kondo configs from dependencies while linting.

Typically, you will want to check copied configs into version control with your project.

See this
[article](https://blog.tvaisanen.com/take-your-linting-game-to-the-next-level?showSharer=true#heading-benefits-of-types-in-the-editor)
by Toni Vaisanen on how to use library configurations and malli!

## Deprecations

Some configuration keys have been renamed over time. The default configuration
is always up-to-date and we strive to maintain backwards compatibility. However,
for completeness, you can find a list of the renamed keys here.

- `:if -> :missing-else-branch`
