GitHub - clj-kondo/clj-kondo: Static analyzer and linter for Clojure code that sparks joy (original) (raw)

Clojars Project Financial Contributors on Open Collective CircleCI Build status cljdoc badge project chat twitter

A static analyzer and linter for Clojure code that sparks joy.

Thanks a lot for clj-kondo. It is like a companion for me. It has made clojure fun again.

@geraldodev on Clojurians Slack

Rationale

Clj-kondo performs static analysis on Clojure, ClojureScript and EDN. It informs you about potential errors while you are typing (without executing your program).

Features

Clj-kondo detects:

before your form hits the REPL.

It suggests several style guide recommendations, such as:

It has support for syntax of commonly used macros likeclojure.core.async/alt!!, schema.core/defn and potemkin/import-vars.

It detects common errors in deps.edn and bb.edn

It provides analysis data so you build your own custom linters.

View all available linters here.

This linter is:

Try clj-kondo at the interactive playground.

Watch the talk:

Clj-kondo at ClojuTRE 2019

Support ❤️

You can support this project via Github Sponsors,OpenCollective,Ko-fi or indirectly via Clojurists Together.

Top sponsors

Installation

Running on the JVM

Running with Docker

Usage

Command line

Lint from stdin:

$ echo '(def x (def x 1))' | clj-kondo --lint - :1:8: warning: inline def

Lint a file:

$ echo '(def x (def x 1))' > /tmp/foo.clj $ clj-kondo --lint /tmp/foo.clj /tmp/foo.clj:1:8: warning: inline def

Lint a directory:

$ clj-kondo --lint src src/clj_kondo/test.cljs:7:1: warning: redundant do src/clj_kondo/calls.clj:291:3: error: Wrong number of args (1) passed to clj-kondo.calls/analyze-calls

Lint a project classpath:

$ clj-kondo --lint "$(lein classpath)"

Help:

$ clj-kondo --help clj-kondo v2024.11.14

Options:

--lint : a file can either be a normal file, directory or classpath. In the case of a directory or classpath, only .clj, .cljs and .cljc will be processed. Use - as filename for reading from stdin.

--lang : if lang cannot be derived from the file extension this option will be used. Supported values: clj, cljs, cljc.

--filename : in case stdin is used for linting, use this to set the reported filename.

--cache-dir: when this option is provided, the cache will be resolved to this directory. If --cache is false, this option will be ignored.

--cache: if false, won't use cache. Otherwise, will try to resolve cache using --cache-dir. If --cache-dir is not set, cache is resolved using the nearest .clj-kondo directory in the current and parent directories.

--config : extra config that is merged. May be a file or an EDN expression. See https://github.com/clj-kondo/clj-kondo/blob/master/doc/config.md.

--config-dir : use this config directory instead of auto-detected .clj-kondo dir.

--parallel: lint sources in parallel.

--dependencies: don't report any findings. Useful for populating cache while linting dependencies.

--copy-configs: copy configs from dependencies while linting.

--skip-lint: skip lint/analysis, still check for other tasks like copy-configs.

--fail-level : minimum severity for exit with error code. Supported values: warning, error. The default level if unspecified is warning.

--report-level : minimum severity for which to report. Supported values: info, warning, error. The default level if unspecified is info.

--debug: print debug information.

Project setup

To detect lint errors across namespaces in your project, a cache is needed. To let clj-kondo know where to create one, make a .clj-kondo directory in the root of your project, meaning on the same level as your project.clj,deps.edn or build.boot:

A cache will be created inside of it when you run clj-kondo. Before linting inside your editor, it is recommended to lint the entire classpath to teachclj-kondo about all the libraries you are using, including Clojure and/or ClojureScript itself. Some libraries come with configurations. To import them, first run:

$ clj-kondo --lint "" --dependencies --copy-configs --skip-lint

The --copy-configs flag will search and copy configurations from dependencies into the.clj-kondo directory, while linting (seeconfig.md).

With the configurations in place, now we can analyze the dependencies properly:

$ clj-kondo --lint "" --dependencies --parallel

The --dependencies flag indicates that clj-kondo is used to analyze sources to populate the cache. When enabled, clj-kondo will suppress warnings and skips over already linted .jar files for performance.

The --parallel option will use multiple threads to lint your sources, going through them faster.

NOTE: in the version after 2024.05.24 copying configs and linting dependencies can be done in one go using:

$ clj-kondo --lint "" --dependencies --parallel --copy-configs

Build tool specific ways to get a classpath:

So for lein the entire command would be:

$ clj-kondo --lint "$(lein classpath)" --dependencies --parallel --copy-configs

Now you are ready to lint single files using editor integration. A simulation of what happens when you edit a file in your editor:

$ echo '(select-keys)' | clj-kondo --lang cljs --lint - :1:1: error: Wrong number of args (0) passed to cljs.core/select-keys

Since clj-kondo now knows about your version of ClojureScript via the cache, it detects that the number of arguments you passed to select-keys is invalid. Each time you edit a file, the cache is incrementally updated, so clj-kondo is informed about new functions you just wrote.

If you want to use a different directory to read and write the cache, use the--cache-dir option. To disable the cache even if you have a .clj-kondodirectory, use --cache false.

Configuration

Editor integration

Exit codes

Exit codes can be controlled by the --fail-level <level> option. The default fail level is warning which returns exit codes as follows:

If --fail-level error is supplied, warnings do not lead to a non-zero exit code:

All exit codes other than 0, 2 and 3 indicate an error because of a bug in clj-kondo or some other unexpected error beyond the control of clj-kondo.

CI Integration

Analysis data

Developer documentation

Companies using clj-kondo

Macros

As clj-kondo is a static analyzer is does not need a runtime (JVM, browser, Node.js, etc.). It doesn't execute your code. As such it can be a faster alternative to linters that do use a runtime, likeeastwood. This approach comes with the limitation that clj-kondo cannot execute your macros as macros can use arbitrary features from a runtime. Clj-kondo has support for clojure core macros and some popular libraries from the community. Macros that are not supported out of the box can be supported usingconfiguration. One of the ways to configure macros is to writehooks for them (also see thisblogpost). For many libraries there is already a configuration available that you canimport. Also check out clj-kondo configs which contains configurations for third party libraries.

Babashka pod

Clj-kondo can be invoked as a babashka pod.

#!/usr/bin/env bb (ns script (:require [babashka.pods :as pods]))

(pods/load-pod "clj-kondo") (require '[pod.borkdude.clj-kondo :as clj-kondo])

(clj-kondo/merge-configs '{:linters {:unresolved-symbol {:exclude [(foo1.bar)]}}} '{:linters {:unresolved-symbol {:exclude [(foo2.bar)]}}}) ;;=> {:linters {:unresolved-symbol {:exclude [(foo1.bar) (foo2.bar)]}}}

(-> (clj-kondo/run! {:lint ["src"]}) :summary) ;;=> {:error 0, :warning 0, :info 0, :type :summary, :duration 779}

Podcasts

Articles

Thanks to:

License

Copyright © 2019 - 2023 Michiel Borkent

Distributed under the EPL License, same as Clojure. See LICENSE.

The directory inlined contains source from tools.reader which is licensed under the EPL license.

The directory parser contains modified source from rewrite-clj which is licensed under the MIT license.