clj-kondo 2025-05-27

Is it feasible to make a clj-kondo linter to warn if you have dependencies listed in project.clj / deps.edn / (whatever boot uses) that are not used in the codebase?

Thank you! I tried to check it out between meetings. Couldn't get it to work (yet) but wrote an issue with the issues I encountered so far: https://github.com/borkdude/unused-deps/issues/2

yep, thanks. provided a partial workaround for better debugging, so please follow up on that when you have time again

@stig328 I believe I found the issue and pushed the fix. re-installing with bbin should work now (until you hit another bug of course)

😄 1
✨ 1

🎉

$ unused-deps --debug 
[...]
{:unused-deps []}

👍 1

Will play more soon

This found an unused dependency in one of my team's projects 🙂

hope not too many false positives ;)

No false positives at all yet.

Scanned about 200kLOC of Clojure so far 🙂

🎉 1

I encountered some false positives with projects that depend solely on other projects for the purpose to bring them in. e.g. weavejester.ragtime exists to bring in ragtime.core + ragtime.jdbc

👍 1

that gave me the idea to turn this tool into something which prints a table instead so you can see how many namespaces + classes you're using from each dependency

or maybe in a tree shape, like lein deps :tree does

👍 1

That sounds really useful

Fun tool, thanks again! It found cljdoc was depending on a lucene dep higher up in the lucene dep tree than necessary. It might be nice to also be able to configure some explanation for deps that are detected as unused to unused-deps that would then be reported by unused-deps. For cljdoc, https://github.com/cljdoc/cljdoc/blob/8adc2d7c31a6d60a71de674c7dff6e7513004517/bb.edn#L157-L180, which outputs this:

[ TASK unused-deps ]-------------------------------------------------------------
Dependencies that do not seem to be referenced by code
NOTE: Update explanation in bb.edn as you see fit

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Dependency                                                                      ┃ Explanation                                                                               ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ [ch.qos.logback/logback-classic {:mvn/version "1.5.18"}]                        ┃ Logging implementation is not directly called from code                                   ┃
┃ [dev.weavejester/ragtime {:mvn/version "0.9.5"}]                                ┃ ragtime API is in its dependencies                                                        ┃
┃ [org.asciidoctor/asciidoctorj {:mvn/version "3.0.0"}]                           ┃ actual API is in dep org.asciidoctor/asciidoctorj-api which we do not explicity depend on ┃
┃ [org.eclipse.jetty/jetty-alpn-client {:mvn/version "11.0.25"}]                  ┃ CVE override                                                                              ┃
┃ [org.eclipse.jetty/jetty-alpn-server {:mvn/version "11.0.25"}]                  ┃ CVE override                                                                              ┃
┃ [org.eclipse.jetty/jetty-servlet {:mvn/version "11.0.25"}]                      ┃ CVE override                                                                              ┃
┃ [org.eclipse.jetty.http2/http2-server {:mvn/version "11.0.25"}]                 ┃ CVE override                                                                              ┃
┃ [org.eclipse.jetty.websocket/websocket-jakarta-server {:mvn/version "11.0.25"}] ┃ CVE override                                                                              ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Total unused deps with no explanation: 0

TASK unused-deps done.

Very nice! Any thoughts on the tree idea @lee? Then we can see that a top level dep pulls in other deps that are used in your project. Although sometimes this is a bad pattern, for some deps it's intentional

Yeah, maybe the tree idea could be interesting. If I understand: So for cljdoc, I could have more easily seen that a lucene higher-level dep cljdoc was referencing was unused by cljdoc and https://github.com/cljdoc/cljdoc/commit/e792b2ae1d26a934613b0313b2f8e2146e5ea41b. It also would have given more info for ragtime and asciidoctorj. But as you say, these particular libs put the API in a transitive dep. For ragtime, maybe it is even better to use the transitive dep directly, but for asciidoctorj, probably not. Is this a different report for the tool though, right? Because we are now reporting on used deps rather than unused deps?

I'm pretty impressed with the tool as it behaves now, to be frank!

we would report how much every dependency on the classpath would be used, with the top level deps being the most interesting ones (declared in deps.edn)

I guess that would also help folks to discover undeclared transitive deps they are using...

1

I guess so? That sounds quite useful.

Would you like a git issue for the explanation thing I described above?

For the tree idea... a pro is the extra info, but this could maybe also be a con, if the user just wants to answer the question "what do I need to fix?". Could the extra info be noisy or overwhelming?

we could just add the tree information on demand and as part of the {:unused-deps ... :tree ...} thing.

issues for both things would be ok I think!

I think so, thanks! I'll get back to this later!

👍 1

Currently this is not a clj-kondo feature, but you can use deps-infer for this, to infer dependencies from source directories and then compare those to the deps you have declared in deps.edn: https://github.com/borkdude/deps-infer

👍 1

Perhaps I can make deps-infer support the above use case more explicitly

❤️ 1

I'll try it out in the meantime. Is it published anywhere, or do I have to download it and run it as a script?

ah, hmm. the existence of clojars.clj gave me a hint 🙂

You can use it as a git dependency. I can add to the README how to do that

1

Thanks, that would be helpful. We're all in on Leiningen here (its author works here...) so I don't know any of the competing tools 😄

is that circleci? :)

Yes 🙂

@stig328 Check this out: https://clojurians.slack.com/archives/C06MAR553/p1748384383090649 Thanks for giving me an idea to hack on during the trainride back from reClojure ;)

🎉 1