{"id":13442371,"url":"https://github.com/clj-kondo/clj-kondo","last_synced_at":"2025-05-11T03:42:34.800Z","repository":{"id":37006241,"uuid":"176829714","full_name":"clj-kondo/clj-kondo","owner":"clj-kondo","description":"Static analyzer and linter for Clojure code that sparks joy","archived":false,"fork":false,"pushed_at":"2025-04-10T13:36:51.000Z","size":14983,"stargazers_count":1763,"open_issues_count":101,"forks_count":292,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-05-07T23:05:38.039Z","etag":null,"topics":["clojure","clojurescript","graalvm","graalvm-native-image","linter","static-analysis"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clj-kondo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"borkdude","open_collective":"clj-kondo","ko_fi":"borkdude","tidelift":null,"custom":null}},"created_at":"2019-03-20T22:58:13.000Z","updated_at":"2025-05-06T17:39:20.000Z","dependencies_parsed_at":"2023-01-17T12:46:37.468Z","dependency_job_id":"bc636d24-065a-425c-ab4e-fdd67b60fde9","html_url":"https://github.com/clj-kondo/clj-kondo","commit_stats":{"total_commits":2333,"total_committers":161,"mean_commits":"14.490683229813664","dds":0.1620231461637377,"last_synced_commit":"97ea8e3c0eddb7809c43248cd4b136baefb1a31e"},"previous_names":["borkdude/clj-kondo"],"tags_count":115,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-kondo%2Fclj-kondo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-kondo%2Fclj-kondo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-kondo%2Fclj-kondo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clj-kondo%2Fclj-kondo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clj-kondo","download_url":"https://codeload.github.com/clj-kondo/clj-kondo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252968117,"owners_count":21833251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","clojurescript","graalvm","graalvm-native-image","linter","static-analysis"],"created_at":"2024-07-31T03:01:44.941Z","updated_at":"2025-05-07T23:05:48.612Z","avatar_url":"https://github.com/clj-kondo.png","language":"Clojure","funding_links":["https://github.com/sponsors/borkdude","https://opencollective.com/clj-kondo","https://ko-fi.com/borkdude"],"categories":["Clojure"],"sub_categories":[],"readme":"\u003cimg src=\"logo/logo-300dpi.png\" width=\"400px\"\u003e\n\n[![Clojars Project](https://img.shields.io/clojars/v/clj-kondo.svg)](https://clojars.org/clj-kondo)\n[![Financial Contributors on Open Collective](https://opencollective.com/clj-kondo/all/badge.svg?label=financial+contributors)](https://opencollective.com/clj-kondo)\n[![CircleCI](https://circleci.com/gh/clj-kondo/clj-kondo/tree/master.svg?style=shield)](https://circleci.com/gh/clj-kondo/clj-kondo/tree/master)\n[![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)\n[![cljdoc badge](https://cljdoc.org/badge/clj-kondo/clj-kondo)](https://cljdoc.org/d/clj-kondo/clj-kondo/CURRENT)\n[![project chat](https://img.shields.io/badge/slack-join_chat-brightgreen.svg)](https://clojurians.slack.com/messages/CHY97NXE2)\n[![twitter](https://img.shields.io/badge/twitter-%23cljkondo-blue)](https://twitter.com/search?q=%23cljkondo\u0026src=typed_query\u0026f=live)\n\nA static analyzer and linter for Clojure code that sparks joy.\n\n\u003cimg src=\"screenshots/demo.png\" width=\"50%\" align=\"right\"\u003e\n\n\u003cblockquote class=\"twitter-tweet\" data-lang=\"en\"\u003e\n    \u003cp lang=\"en\" dir=\"ltr\"\u003eThanks a lot for clj-kondo. It is like a companion for me. It has made clojure fun again.\u003c/p\u003e\n    \u0026mdash;\n    \u003ca href=\"https://github.com/geraldodev\"\u003e@geraldodev\u003c/a\u003e on Clojurians Slack\n\u003c/blockquote\u003e\n\n## Rationale\n\nClj-kondo performs [static analysis]() on Clojure, ClojureScript and EDN. It\ninforms you about potential errors while you are typing (without executing your\nprogram).\n\n## Features\n\nClj-kondo detects:\n\n* inline `def` expressions\n* redundant `do` and `let` wrappings\n* arity errors:\n  - within the same namespace and across namespaces\n  - of static Java method calls\n  - of local `let` and `letfn` binding calls\n  - of recursive calls (including `recur`)\n  - conflicting arities in overloaded functions\n* unused private vars\n* private and deprecated var usage\n* required but unused namespaces\n* unsorted required namespaces\n* referred but unused vars\n* duplicate requires\n* unused function arguments and let bindings\n* marked as unused, but used arguments and let bindings (optional)\n* unused imports\n* redefined vars\n* unresolved symbols, vars and namespaces\n* misplaced docstrings\n* duplicate map keys and set elements\n* duplicates and quoting in case test constants\n* missing map keys\n* invalid number of forms in binding vectors\n* missing assertions in `clojure.test/deftest`\n* alias consistency\n* [type checking](doc/types.md)\n* Datalog syntax checking\n* format string argument mismatches\n* shadowed vars\n* 2 argument usage of reduce (optional)\n\nbefore your form hits the REPL.\n\nIt suggests several style guide recommendations, such as:\n\n* rules from Stuart Sierra's [how to ns](https://stuartsierra.com/2016/clojure-how-to-ns.html)\n* 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))\n* use `seq` instead of `(not (empty? ,,,))` (see [Clojure style guide](https://github.com/bbatsov/clojure-style-guide#nil-punning))\n* don't make your lines too long (see [Clojure style guide](https://github.com/bbatsov/clojure-style-guide#80-character-limits))\n\n\u003cimg src=\"screenshots/wrong-arity.png\" width=\"50%\" align=\"right\"\u003e\n\nIt has support for syntax of commonly used macros like\n`clojure.core.async/alt!!`, `schema.core/defn` and `potemkin/import-vars`.\n\nIt detects common errors in `deps.edn` and `bb.edn`\n\nIt provides [analysis data](analysis) so you build your own custom linters.\n\nView all available linters [here](doc/linters.md).\n\nThis linter is:\n\n* compatible with `.clj`, `.cljs`, `.cljc` and `.edn` files\n* build tool and editor agnostic\n* a static code analyzer\n* compiled to native code using GraalVM\n\nTry clj-kondo at the [interactive playground](https://clj-kondo.michielborkent.nl).\n\nWatch the talk:\n\n[![Clj-kondo at ClojuTRE 2019](https://img.youtube.com/vi/MB3vVREkI7s/0.jpg)](https://www.youtube.com/watch?v=MB3vVREkI7s)\n\n## Support :heart:\n\nYou can support this project via [Github\nSponsors](https://github.com/sponsors/borkdude),\n[OpenCollective](https://opencollective.com/clj-kondo),\n[Ko-fi](https://ko-fi.com/borkdude) or indirectly via [Clojurists\nTogether](https://www.clojuriststogether.org/).\n\n\u003cdetails\u003e\n\n\u003csummary\u003eTop sponsors\u003c/summary\u003e\n\n- [Clojurists Together](https://clojuriststogether.org/)\n- [Roam Research](https://roamresearch.com/)\n- [Nextjournal](https://nextjournal.com/)\n- [Toyokumo](https://toyokumo.co.jp/)\n- [Cognitect](https://www.cognitect.com/)\n- [Kepler16](https://kepler16.com/)\n- [Adgoji](https://www.adgoji.com/)\n\n\u003c/details\u003e\n\n## [Installation](doc/install.md)\n\n## [Running on the JVM](doc/jvm.md)\n\n## [Running with Docker](doc/docker.md)\n\n## Usage\n\n### Command line\n\nLint from stdin:\n\n``` shellsession\n$ echo '(def x (def x 1))' | clj-kondo --lint -\n\u003cstdin\u003e:1:8: warning: inline def\n```\n\nLint a file:\n\n``` shellsession\n$ echo '(def x (def x 1))' \u003e /tmp/foo.clj\n$ clj-kondo --lint /tmp/foo.clj\n/tmp/foo.clj:1:8: warning: inline def\n```\n\nLint a directory:\n\n``` shellsession\n$ clj-kondo --lint src\nsrc/clj_kondo/test.cljs:7:1: warning: redundant do\nsrc/clj_kondo/calls.clj:291:3: error: Wrong number of args (1) passed to clj-kondo.calls/analyze-calls\n```\n\nLint a project classpath:\n\n``` shellsession\n$ clj-kondo --lint \"$(lein classpath)\"\n```\n\nHelp:\n``` shellsession\n$ clj-kondo --help\nclj-kondo v2024.11.14\n\nOptions:\n\n--lint \u003cfile\u003e: a file can either be a normal file, directory or classpath. In the\ncase of a directory or classpath, only .clj, .cljs and .cljc will be\nprocessed. Use - as filename for reading from stdin.\n\n--lang \u003clang\u003e: if lang cannot be derived from the file extension this option will be\nused. Supported values: clj, cljs, cljc.\n\n--filename \u003cfile\u003e: in case stdin is used for linting, use this to set the\nreported filename.\n\n--cache-dir: when this option is provided, the cache will be resolved to this\ndirectory. If --cache is false, this option will be ignored.\n\n--cache: if false, won't use cache. Otherwise, will try to resolve cache\nusing `--cache-dir`. If `--cache-dir` is not set, cache is resolved using the\nnearest `.clj-kondo` directory in the current and parent directories.\n\n--config \u003cconfig\u003e: 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.\n\n--config-dir \u003cconfig-dir\u003e: use this config directory instead of auto-detected\n.clj-kondo dir.\n\n--parallel: lint sources in parallel.\n\n--dependencies: don't report any findings. Useful for populating cache while linting dependencies.\n\n--copy-configs: copy configs from dependencies while linting.\n\n--skip-lint: skip lint/analysis, still check for other tasks like copy-configs.\n\n--fail-level \u003clevel\u003e: minimum severity for exit with error code.  Supported values:\nwarning, error.  The default level if unspecified is warning.\n\n--report-level \u003clevel\u003e: minimum severity for which to report.  Supported values:\ninfo, warning, error.  The default level if unspecified is info.\n\n--debug: print debug information.\n```\n\n## Project setup\n\nTo detect lint errors across namespaces in your project, a cache is needed. To\nlet clj-kondo know where to create one, make a `.clj-kondo` directory in the\nroot of your project, meaning on the same level as your `project.clj`,\n`deps.edn` or `build.boot`:\n\n``` clojure\n$ mkdir -p .clj-kondo\n```\n\nA cache will be created inside of it when you run `clj-kondo`. Before linting\ninside your editor, it is recommended to lint the entire classpath to teach\n`clj-kondo` about all the libraries you are using, including Clojure and/or\nClojureScript itself. Some libraries come with configurations. To import them, first run:\n\n``` shellsession\n$ clj-kondo --lint \"\u003cclasspath\u003e\" --dependencies --copy-configs --skip-lint\n```\n\nThe `--copy-configs` flag will search and copy configurations from dependencies into the\n`.clj-kondo` directory, while linting (see\n[config.md](doc/config.md#exporting-and-importing-configuration)).\n\nWith the configurations in place, now we can analyze the dependencies properly:\n\n``` shellsession\n$ clj-kondo --lint \"\u003cclasspath\u003e\" --dependencies --parallel\n```\n\nThe `--dependencies` flag indicates that clj-kondo is used to analyze sources to\npopulate the cache. When enabled, clj-kondo will suppress warnings and skips over\nalready linted `.jar` files for performance.\n\nThe `--parallel` option will use multiple threads to lint your sources, going through them faster.\n\nNOTE: in the version after `2024.05.24` copying configs and linting dependencies can be done in one go using:\n\n``` shellsession\n$ clj-kondo --lint \"\u003cclasspath\u003e\" --dependencies --parallel --copy-configs\n```\n\nBuild tool specific ways to get a classpath:\n- `lein classpath`\n- `boot with-cp -w -f -`\n- `clojure -Spath`\n- `npx shadow-cljs classpath`\n\nSo for `lein` the entire command would be:\n\n    $ clj-kondo --lint \"$(lein classpath)\" --dependencies --parallel --copy-configs\n\nNow you are ready to lint single files using [editor\nintegration](doc/editor-integration.md). A simulation of what happens when you\nedit a file in your editor:\n\n``` shellsession\n$ echo '(select-keys)' | clj-kondo --lang cljs --lint -\n\u003cstdin\u003e:1:1: error: Wrong number of args (0) passed to cljs.core/select-keys\n```\n\nSince clj-kondo now knows about your version of ClojureScript via the cache,\nit detects that the number of arguments you passed to `select-keys` is\ninvalid. Each time you edit a file, the cache is incrementally updated, so\nclj-kondo is informed about new functions you just wrote.\n\nIf you want to use a different directory to read and write the cache, use the\n`--cache-dir` option. To disable the cache even if you have a `.clj-kondo`\ndirectory, use `--cache false`.\n\n## [Configuration](doc/config.md)\n\n## [Editor integration](doc/editor-integration.md)\n\n## Exit codes\n\nExit codes can be controlled by the `--fail-level \u003clevel\u003e` option. The\ndefault fail level is `warning` which returns exit codes as follows:\n\n- `0`: no errors or warnings were found\n- `2`: one or more warnings were found\n- `3`: one or more errors were found\n\nIf `--fail-level error` is supplied, warnings do not lead to a non-zero exit code:\n\n- `0`: no errors were found\n- `0`: one or more warnings were found\n- `3`: one or more errors were found\n\nAll exit codes other than `0`, `2` and `3` indicate an error because of a bug in\nclj-kondo or some other unexpected error beyond the control of clj-kondo.\n\n## [CI Integration](doc/ci-integration.md)\n## [Analysis data](analysis)\n\n## [Developer documentation](doc/dev.md)\n\n## [Companies](doc/companies.md) using clj-kondo\n\n## Macros\n\nAs clj-kondo is a static analyzer is does not need a runtime (JVM, browser,\nNode.js, etc.). It doesn't execute your code. As such it can be a faster\nalternative to linters that do use a runtime, like\n[eastwood](https://github.com/jonase/eastwood). This approach comes with the\nlimitation that clj-kondo cannot execute your macros as macros can use arbitrary\nfeatures from a runtime. Clj-kondo has support for clojure core macros and some\npopular libraries from the community. Macros that are not supported out of the\nbox can be supported using\n[configuration](https://github.com/clj-kondo/clj-kondo/blob/master/doc/config.md#unrecognized-macros). One\nof the ways to configure macros is to write\n[hooks](https://github.com/clj-kondo/clj-kondo/blob/master/doc/hooks.md) for\nthem (also see this\n[blogpost](https://blog.michielborkent.nl/clj-kondo-hooks.html)).\nFor many libraries there is already a configuration available that you can\n[import](https://github.com/clj-kondo/clj-kondo/blob/master/doc/config.md#importing). Also\ncheck out clj-kondo [configs](https://github.com/clj-kondo/configs) which\ncontains configurations for third party libraries.\n\n## Babashka pod\n\nClj-kondo can be invoked as a [babashka\npod](https://github.com/babashka/babashka.pods).\n\n``` clojure\n#!/usr/bin/env bb\n(ns script\n  (:require [babashka.pods :as pods]))\n\n(pods/load-pod \"clj-kondo\")\n(require '[pod.borkdude.clj-kondo :as clj-kondo])\n\n(clj-kondo/merge-configs\n '{:linters {:unresolved-symbol {:exclude [(foo1.bar)]}}}\n '{:linters {:unresolved-symbol {:exclude [(foo2.bar)]}}})\n;;=\u003e {:linters {:unresolved-symbol {:exclude [(foo1.bar) (foo2.bar)]}}}\n\n(-\u003e (clj-kondo/run! {:lint [\"src\"]})\n    :summary)\n;;=\u003e {:error 0, :warning 0, :info 0, :type :summary, :duration 779}\n```\n\n## Podcasts\n\n+ [defnpodcast](https://soundcloud.com/defn-771544745)\n+ [ClojureScript Podcast](https://clojurescriptpodcast.com/)\n\n## Articles\n\n- [How to catch derived Vars with a clj-kondo hook](https://www.mikkokoski.com/blog/derived-vars/index.html) by Mikko Koski\n- [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\n\n## Thanks to:\n\n- [joker](https://github.com/candid82/joker) for inspiration\n- [rewrite-clj](https://github.com/xsc/rewrite-clj) for the Clojure parser code\n- [eastwood](https://github.com/jonase/eastwood) for `var-info.edn` and inspiration\n- [contributors](https://github.com/clj-kondo/clj-kondo/graphs/contributors) and\n  other users posting issues with bug reports and ideas\n- [Nikita Prokopov](https://github.com/tonsky) for the logo\n- [adgoji](https://www.adgoji.com/) for financial support\n- [Clojurists Together](https://www.clojuriststogether.org/) for sponsoring work\n  on hooks\n\n## License\n\nCopyright © 2019 - 2023 Michiel Borkent\n\nDistributed under the EPL License, same as Clojure. See LICENSE.\n\nThe directory `inlined` contains source from [`tools.reader`](https://github.com/clojure/tools.reader) which is licensed under the EPL license.\n\nThe directory `parser` contains modified source from [`rewrite-clj`](https://github.com/xsc/rewrite-clj) which is licensed under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclj-kondo%2Fclj-kondo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclj-kondo%2Fclj-kondo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclj-kondo%2Fclj-kondo/lists"}