TL;DR
目前影响 clojure.tools.namespace.repl/refresh
加载哪些源文件的唯一方法是设置刷新目录,这基于一个允许列表的系统。目前还没有方法来阻止加载特定的目录/文件/模式,而允许其他所有内容。当类路径上有一些 Clojure 源文件位于源目录中,而这些文件根本不应该被加载时,这会引发问题。
长篇大论
我们遇到了与 clojure.tools.namespace/refresh
及 clj-kondo 钩子 相关的有趣问题。
一个单行代码示例
clj -Srepro -Sdeps '{:deps {org.clojure/tools.namespace {:mvn/version "1.2.0"} seancorfield/next.jdbc {:git/url "https://github.com/seancorfield/next-jdbc/" :git/sha "24bf1dbaa441d62461f980e9f880df5013f295dd"}}}' -M -e "((requiring-resolve 'clojure.tools.namespace.repl/refresh-all))"
这将会失败
:error-while-loading hooks.com.github.seancorfield.next-jdbc
Could not locate hooks/com/github/seancorfield/next_jdbc__init.class, hooks/com/github/seancorfield/next_jdbc.clj or hooks/com/github/seancorfield/next_jdbc.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
为了背景,clj-kondo 是一个静态分析器/linter。为了能够正确分析自定义宏,它允许库将描述这些宏应该如何被分析的字节码文件(clj)作为资源在特定目录下分发。
上述示例失败是因为以下原因的结合
因为没有方法可以告诉工具命名空间不加载某些文件,我不得不想出一个稍微噱头的解决方案,这尝试将刷新目录设置为类路径目录减去有问题的那些,但如果有一个方法可以通过拒绝列表或谓词来设置这个值会更好。
解决方案 以备他人遇到相同问题
(defn remove-clj-kondo-exports-from-tools-ns-refresh-dirs
"A potential issue from using this is that if the directory containing the clj-kondo.exports folder
also directly contains to-be-reloaded clojure source files, those will no longer be reloaded."
[]
(->> (clojure.java.classpath/classpath-directories)
(mapcat
(fn [^File classpath-directory]
(let [children (.listFiles classpath-directory)
directory? #(.isDirectory ^File %)
clj-kondo-exports?
#(= "clj-kondo.exports" (.getName ^File %))
has-clj-kondo-exports
(some (every-pred clj-kondo-exports? directory?) children)]
(if has-clj-kondo-exports
(->> children
(filter directory?)
(remove clj-kondo-exports?))
[classpath-directory]))))
(apply clojure.tools.namespace.repl/set-refresh-dirs)))
;; call in user.clj
(remove-clj-kondo-exports-from-tools-ns-refresh-dirs)