Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/clj-kondo/clj-kondo
Static analyzer and linter for Clojure code that sparks joy
https://github.com/clj-kondo/clj-kondo
clojure clojurescript graalvm graalvm-native-image linter static-analysis
Last synced: 14 days ago
JSON representation
Static analyzer and linter for Clojure code that sparks joy
- Host: GitHub
- URL: https://github.com/clj-kondo/clj-kondo
- Owner: clj-kondo
- License: epl-1.0
- Created: 2019-03-20T22:58:13.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2024-04-30T16:11:29.000Z (7 months ago)
- Last Synced: 2024-05-01T20:30:19.745Z (6 months ago)
- Topics: clojure, clojurescript, graalvm, graalvm-native-image, linter, static-analysis
- Language: Clojure
- Homepage:
- Size: 13.5 MB
- Stars: 1,661
- Watchers: 20
- Forks: 290
- Open Issues: 116
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-list - clj-kondo - kondo | 1198 | (Clojure)
README
[![Clojars Project](https://img.shields.io/clojars/v/clj-kondo.svg)](https://clojars.org/clj-kondo)
[![Financial Contributors on Open Collective](https://opencollective.com/clj-kondo/all/badge.svg?label=financial+contributors)](https://opencollective.com/clj-kondo)
[![CircleCI](https://circleci.com/gh/clj-kondo/clj-kondo/tree/master.svg?style=shield)](https://circleci.com/gh/clj-kondo/clj-kondo/tree/master)
[![Build status](https://ci.appveyor.com/api/projects/status/3jdro7mu17nv3rb3/branch/master?svg=true)](https://ci.appveyor.com/project/clj-kondo/clj-kondo/branch/master)
[![cljdoc badge](https://cljdoc.org/badge/clj-kondo/clj-kondo)](https://cljdoc.org/d/clj-kondo/clj-kondo/CURRENT)
[![project chat](https://img.shields.io/badge/slack-join_chat-brightgreen.svg)](https://clojurians.slack.com/messages/CHY97NXE2)
[![twitter](https://img.shields.io/badge/twitter-%23cljkondo-blue)](https://twitter.com/search?q=%23cljkondo&src=typed_query&f=live)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:
* inline `def` expressions
* redundant `do` and `let` wrappings
* arity errors:
- within the same namespace and across namespaces
- of static Java method calls
- of local `let` and `letfn` binding calls
- of recursive calls (including `recur`)
- conflicting arities in overloaded functions
* unused private vars
* private and deprecated var usage
* required but unused namespaces
* unsorted required namespaces
* referred but unused vars
* duplicate requires
* unused function arguments and let bindings
* marked as unused, but used arguments and let bindings (optional)
* unused imports
* redefined vars
* unresolved symbols, vars and namespaces
* misplaced docstrings
* duplicate map keys and set elements
* duplicates and quoting in case test constants
* missing map keys
* invalid number of forms in binding vectors
* missing assertions in `clojure.test/deftest`
* alias consistency
* [type checking](doc/types.md)
* Datalog syntax checking
* format string argument mismatches
* shadowed vars
* 2 argument usage of reduce (optional)before your form hits the REPL.
It suggests several style guide recommendations, such as:
* rules from Stuart Sierra's [how to ns](https://stuartsierra.com/2016/clojure-how-to-ns.html)
* use `:else` as the catch-all test expression in `cond` (see [Clojure style guide](https://github.com/bbatsov/clojure-style-guide#else-keyword-in-cond))
* use `seq` instead of `(not (empty? ,,,))` (see [Clojure style guide](https://github.com/bbatsov/clojure-style-guide#nil-punning))
* don't make your lines too long (see [Clojure style guide](https://github.com/bbatsov/clojure-style-guide#80-character-limits))It has support for syntax of commonly used macros like
`clojure.core.async/alt!!`, `schema.core/defn` and `potemkin/import-vars`.It detects common errors in `deps.edn` and `bb.edn`
It provides [analysis data](analysis) so you build your own custom linters.
View all available linters [here](doc/linters.md).
This linter is:
* compatible with `.clj`, `.cljs`, `.cljc` and `.edn` files
* build tool and editor agnostic
* a static code analyzer
* compiled to native code using GraalVMTry clj-kondo at the [interactive playground](https://clj-kondo.michielborkent.nl).
Watch the talk:
[![Clj-kondo at ClojuTRE 2019](https://img.youtube.com/vi/MB3vVREkI7s/0.jpg)](https://www.youtube.com/watch?v=MB3vVREkI7s)
## Support :heart:
You can support this project via [Github
Sponsors](https://github.com/sponsors/borkdude),
[OpenCollective](https://opencollective.com/clj-kondo),
[Ko-fi](https://ko-fi.com/borkdude) or indirectly via [Clojurists
Together](https://www.clojuriststogether.org/).Top sponsors
- [Clojurists Together](https://clojuriststogether.org/)
- [Roam Research](https://roamresearch.com/)
- [Nextjournal](https://nextjournal.com/)
- [Toyokumo](https://toyokumo.co.jp/)
- [Cognitect](https://www.cognitect.com/)
- [Kepler16](https://kepler16.com/)
- [Adgoji](https://www.adgoji.com/)## [Installation](doc/install.md)
## [Running on the JVM](doc/jvm.md)
## [Running with Docker](doc/docker.md)
## Usage
### Command line
Lint from stdin:
``` shellsession
$ echo '(def x (def x 1))' | clj-kondo --lint -
:1:8: warning: inline def
```Lint a file:
``` shellsession
$ 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:
``` shellsession
$ 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:
``` shellsession
$ clj-kondo --lint "$(lein classpath)"
```## 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`:``` clojure
$ mkdir -p .clj-kondo
```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 teach
`clj-kondo` about all the libraries you are using, including Clojure and/or
ClojureScript itself. Some libraries come with configurations. To import them, first run:``` shellsession
$ 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 (see
[config.md](doc/config.md#exporting-and-importing-configuration)).With the configurations in place, now we can analyze the dependencies properly:
``` shellsession
$ 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:
``` shellsession
$ clj-kondo --lint "" --dependencies --parallel --copy-configs
```Build tool specific ways to get a classpath:
- `lein classpath`
- `boot with-cp -w -f -`
- `clojure -Spath`
- `npx shadow-cljs 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](doc/editor-integration.md). A simulation of what happens when you
edit a file in your editor:``` shellsession
$ 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-kondo`
directory, use `--cache false`.## [Configuration](doc/config.md)
## [Editor integration](doc/editor-integration.md)
## Exit codes
Exit codes can be controlled by the `--fail-level ` option. The
default fail level is `warning` which returns exit codes as follows:- `0`: no errors or warnings were found
- `2`: one or more warnings were found
- `3`: one or more errors were foundIf `--fail-level error` is supplied, warnings do not lead to a non-zero exit code:
- `0`: no errors were found
- `0`: one or more warnings were found
- `3`: one or more errors were foundAll 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](doc/ci-integration.md)
## [Analysis data](analysis)## [Developer documentation](doc/dev.md)
## [Companies](doc/companies.md) 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, like
[eastwood](https://github.com/jonase/eastwood). 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 using
[configuration](https://github.com/clj-kondo/clj-kondo/blob/master/doc/config.md#unrecognized-macros). One
of the ways to configure macros is to write
[hooks](https://github.com/clj-kondo/clj-kondo/blob/master/doc/hooks.md) for
them (also see this
[blogpost](https://blog.michielborkent.nl/clj-kondo-hooks.html)).
For many libraries there is already a configuration available that you can
[import](https://github.com/clj-kondo/clj-kondo/blob/master/doc/config.md#importing). Also
check out clj-kondo [configs](https://github.com/clj-kondo/configs) which
contains configurations for third party libraries.## Babashka pod
Clj-kondo can be invoked as a [babashka
pod](https://github.com/babashka/babashka.pods).``` clojure
#!/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
+ [defnpodcast](https://soundcloud.com/defn-771544745)
+ [ClojureScript Podcast](https://clojurescriptpodcast.com/)## Articles
- [How to catch derived Vars with a clj-kondo hook](https://www.mikkokoski.com/blog/derived-vars/index.html) by Mikko Koski
- [Taking your linting to the next level](https://blog.tvaisanen.com/take-your-linting-game-to-the-next-level?showSharer=true#heading-benefits-of-types-in-the-editor) by Toni Vaisanen## Thanks to:
- [joker](https://github.com/candid82/joker) for inspiration
- [rewrite-clj](https://github.com/xsc/rewrite-clj) for the Clojure parser code
- [eastwood](https://github.com/jonase/eastwood) for `var-info.edn` and inspiration
- [contributors](https://github.com/clj-kondo/clj-kondo/graphs/contributors) and
other users posting issues with bug reports and ideas
- [Nikita Prokopov](https://github.com/tonsky) for the logo
- [adgoji](https://www.adgoji.com/) for financial support
- [Clojurists Together](https://www.clojuriststogether.org/) for sponsoring work
on hooks## License
Copyright © 2019 - 2023 Michiel Borkent
Distributed under the EPL License, same as Clojure. See LICENSE.
The directory `inlined` contains source from [`tools.reader`](https://github.com/clojure/tools.reader) which is licensed under the EPL license.
The directory `parser` contains modified source from [`rewrite-clj`](https://github.com/xsc/rewrite-clj) which is licensed under the MIT license.