Skip to content

Commit 9089875

Browse files
authored
Honor clj-kondo's namespace local configuration (#388)
1 parent 74dada9 commit 9089875

File tree

9 files changed

+111
-38
lines changed

9 files changed

+111
-38
lines changed

.clj-kondo/config.edn

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,9 @@
4444
:unresolved-var {:exclude [org.httpkit.client/get]}
4545
;; for integration tests:
4646
:unused-namespace {:exclude [sample.unused.namespace
47-
"more.unused.namespaces*"]}}}
47+
"more.unused.namespaces*"]}}
48+
49+
:config-in-ns {;; for integration tests:
50+
refactor-nrepl.ns.libspec-allowlist-test
51+
{:linters {:unused-namespace {:exclude [from-config-in-ns
52+
"^from-config-in-ns.re*"]}}}}}

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Unreleased
44

5+
* [#387](https://github.com/clojure-emacs/refactor-nrepl/issues/387): extend clj-kondo `:unused-namespace` integration. Now namespace local configuration is also taken into account.
6+
`:libspec-whitelist` can be augmented for particular namespace by:
7+
* Adding `:unused-namespace` linter configuration under `:config-in-ns` key in clj-kondo's config file. Like so: `:config-in-ns {example.target.ns {:linters {:unused-namespace {:exclude [ns.to.exclude]}}}}`
8+
* Adding `:unused-namespace` linter configuration under `:clj-kondo/config` key in metadata or attribute map of namespace. Like so: `(ns example.target.ns {:clj-kondo/config '{:linters {:unused-namespace {:exclude [ns.to.exclude]}}}})`
9+
510
## 3.5.5
611

712
* [#385](https://github.com/clojure-emacs/refactor-nrepl/pull/385): only `suggest-aliases` that are valid symbols.

src/refactor_nrepl/core.clj

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,14 @@
365365
(StringReader.)
366366
(PushbackReader.)
367367
parse/read-ns-decl)
368-
ns-meta (meta (second ns-form))]
368+
ns-meta (meta (second ns-form))
369+
attr-map (->> ns-form
370+
(drop 2)
371+
(take 2)
372+
(some (fn [e] (when (map? e) e))))]
369373
{:top-level-meta ns-meta
370-
:gc-methods-meta (extract-gen-class-methods-meta ns-form)}))
374+
:gc-methods-meta (extract-gen-class-methods-meta ns-form)
375+
:attr-map attr-map}))
371376

372377
(defn read-ns-form-with-meta
373378
"Read the ns form found at PATH.

src/refactor_nrepl/ns/libspec_allowlist.clj

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,33 @@
66
(clojure.lang IFn)
77
(java.util.regex Pattern)))
88

9-
(defn- libspec-allowlist* []
10-
(let [kondo-file (io/file ".clj-kondo" "config.edn")
11-
exclude (when (.exists kondo-file)
12-
(try
13-
(-> kondo-file slurp read-string :linters :unused-namespace :exclude)
14-
(catch Exception e
15-
(when (System/getenv "CI")
16-
(throw e)))))]
17-
(->> exclude
18-
(mapv (fn [entry]
19-
(if (symbol? entry)
20-
(str "^" (Pattern/quote (str entry)) "$")
21-
entry)))
22-
(into (:libspec-whitelist config/*config*)))))
9+
(defn maybe-unwrap-quote [obj]
10+
(if (and (seq? obj) (= 'quote (first obj)))
11+
(second obj)
12+
obj))
13+
14+
(defn- kondo-excludes [{namespace-name :ns
15+
ns-meta :meta}]
16+
(let [linter-path [:linters :unused-namespace :exclude]
17+
local-config (-> ns-meta :clj-kondo/config maybe-unwrap-quote)
18+
kondo-file (io/file ".clj-kondo" "config.edn")
19+
config (when (.exists kondo-file)
20+
(try
21+
(-> kondo-file slurp read-string)
22+
(catch Exception e
23+
(when (System/getenv "CI")
24+
(throw e)))))]
25+
(reduce into [(get-in config linter-path)
26+
(get-in config (into [:config-in-ns namespace-name] linter-path))
27+
(get-in local-config linter-path)])))
28+
29+
(defn- libspec-allowlist* [current-ns]
30+
(->> (kondo-excludes current-ns)
31+
(mapv (fn [entry]
32+
(if (symbol? entry)
33+
(str "^" (Pattern/quote (str entry)) "$")
34+
entry)))
35+
(into (:libspec-whitelist config/*config*))))
2336

2437
(def ^:private ^:dynamic ^IFn *libspec-allowlist* nil)
2538

@@ -32,9 +45,9 @@
3245
with clj-kondo's `:unused-namespace` config.
3346
3447
Uses a memoized version if available."
35-
[]
36-
(or (some-> *libspec-allowlist* .invoke)
37-
(libspec-allowlist*)))
48+
[current-ns]
49+
(or (some-> *libspec-allowlist* (.invoke current-ns))
50+
(libspec-allowlist* current-ns)))
3851

3952
(defmacro with-memoized-libspec-allowlist
4053
"Memoizes the libspec-allowlist internals while `body` is executing.

src/refactor_nrepl/ns/ns_parser.clj

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,16 @@
112112
(parse-clj-or-cljs-ns path :cljs)))
113113

114114
(defn parse-ns [path-or-file]
115-
(assoc
116-
(if (core/cljc-file? (io/file path-or-file))
117-
(parse-cljc-ns path-or-file)
118-
(parse-clj-or-cljs-ns path-or-file))
119-
:ns (second (core/read-ns-form-with-meta path-or-file))
120-
:source-dialect (core/file->dialect path-or-file)))
115+
(let [[_ namespace-name :as ns-form] (core/read-ns-form-with-meta path-or-file)
116+
select-metadata (juxt :top-level-meta :attr-map)
117+
metadata (-> ns-form meta select-metadata)]
118+
(assoc
119+
(if (core/cljc-file? (io/file path-or-file))
120+
(parse-cljc-ns path-or-file)
121+
(parse-clj-or-cljs-ns path-or-file))
122+
:ns namespace-name
123+
:meta (apply merge metadata)
124+
:source-dialect (core/file->dialect path-or-file))))
121125

122126
(def ^:dynamic *read-ns-form-with-meta* core/read-ns-form-with-meta)
123127

src/refactor_nrepl/ns/prune_dependencies.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,11 @@
124124
;; pruned.
125125
(defn libspec-should-never-be-pruned?
126126
"Should `libspec` never be pruned away by the `clean-ns` op?"
127-
[libspec]
127+
[current-ns libspec]
128128
(let [ns-name (str (:ns libspec))]
129129
(boolean (some (fn [^String pattern]
130130
(-> pattern re-pattern (re-find ns-name)))
131-
(libspec-allowlist/libspec-allowlist)))))
131+
(libspec-allowlist/libspec-allowlist current-ns)))))
132132

133133
(defn imports->namespaces
134134
"Given a collection of `:import` clauses, returns the set of namespaces denoted by them, as symbols.
@@ -185,7 +185,7 @@
185185

186186
(defn- prune-libspec [symbols-in-file current-ns imports-namespaces libspec]
187187
(cond
188-
(libspec-should-never-be-pruned? libspec)
188+
(libspec-should-never-be-pruned? current-ns libspec)
189189
libspec
190190

191191
(imports-contain-libspec? imports-namespaces (:ns libspec))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(ns ^:foo ^:bar ^{:a 1, :b 2} ns-with-meta-and-attr-map {:c 3, :d 4}
2+
(:require [clojure.test :as t]))

test/refactor_nrepl/core_test.clj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,13 @@
7777
(testing "`:as-alias` directives are kept"
7878
(is (= '(ns as-alias (:require [foo :as-alias f]))
7979
(sut/read-ns-form-with-meta "test-resources/as_alias.clj")))))
80+
81+
(deftest extract-ns-meta
82+
(testing "namespace metadata and attr-map are extracted and merged together"
83+
(let [ns-meta (sut/extract-ns-meta (slurp "test-resources/ns_with_meta_and_attr_map.clj"))]
84+
(is (= {:a 1
85+
:b 2
86+
:bar true
87+
:foo true}
88+
(:top-level-meta ns-meta)))
89+
(is (= {:c 3, :d 4} (:attr-map ns-meta))))))
Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
(ns refactor-nrepl.ns.libspec-allowlist-test
2+
{:clj-kondo/config '{:linters {:unused-namespace {:exclude [from-ns-attr-map
3+
"from-ns-attr-map.re*"]}}}}
24
(:require
35
[clojure.test :refer [are deftest is testing]]
46
[refactor-nrepl.ns.libspec-allowlist :as sut]
5-
[refactor-nrepl.ns.prune-dependencies :as prune-dependencies]))
7+
[refactor-nrepl.ns.prune-dependencies :as prune-dependencies]
8+
[refactor-nrepl.ns.ns-parser :refer [parse-ns]]))
9+
10+
(def this-ns (parse-ns "test/refactor_nrepl/ns/libspec_allowlist_test.clj"))
611

712
(deftest libspec-allowlist-test
813
(testing "Takes into account refactor-nrepls own config, and .clj-kondo/config files alike,
@@ -12,26 +17,50 @@ merging their results"
1217
;; from our .clj-kondo file - symbols become quoted patterns:
1318
"^\\Qsample.unused.namespace\\E$"
1419
;; from our .clj-kondo file - strings have 'regex' semantics so are kept as-is:
15-
"more.unused.namespaces*"]
20+
"more.unused.namespaces*"
21+
;; from our .clj-konfo file, namespace local configuration
22+
"^\\Qfrom-config-in-ns\\E$"
23+
"^from-config-in-ns.re*"
24+
;; from attr-map of this ns
25+
"^\\Qfrom-ns-attr-map\\E$"
26+
"from-ns-attr-map.re*"]
1627

17-
(sut/libspec-allowlist)))
28+
(sut/libspec-allowlist this-ns)))
1829

19-
(is (every? string? (sut/libspec-allowlist))
30+
(is (every? string? (sut/libspec-allowlist this-ns))
2031
"Items coming from different sources all have the same class,
2132
ensuring they will be treated homogeneously by refactor-nrepl")
2233

2334
(testing "`libspec-should-never-be-pruned?` is integrated with clj-kondo logic,
24-
effecively parsing its config into well-formed regexes"
35+
effectively parsing its config into well-formed regexes"
2536
(are [input expected] (= expected
26-
(prune-dependencies/libspec-should-never-be-pruned? {:ns input}))
37+
(prune-dependencies/libspec-should-never-be-pruned? this-ns {:ns input}))
2738
'sample.unused.namespace true
2839
'Asample.unused.namespace false
2940
'sample.unused.namespaceB false
3041
'more.unused.namespaces true
3142
'more.unused.namespacessss true
32-
'more.unused.namespac false))
43+
'more.unused.namespac false
44+
'from-config-in-ns true
45+
'from-config-in-ns.core false
46+
'from-config-in-ns.re true
47+
'from-config-in-ns.re.f true
48+
'Bfrom-config-in-ns.re false
49+
'from-ns-attr-map true
50+
'Efrom-ns-attr-map false
51+
'from-ns-attr-map.re true
52+
'from-ns-attr-map.re.f true))
3353

3454
(testing "Always returns a sequence, memoized or not"
3555
(is (seq (sut/with-memoized-libspec-allowlist
36-
(sut/libspec-allowlist))))
37-
(is (seq (sut/libspec-allowlist))))))
56+
(sut/libspec-allowlist this-ns))))
57+
(is (seq (sut/libspec-allowlist this-ns))))))
58+
59+
(deftest maybe-unwrap-quote
60+
(testing "unwraps object if it is quoted, returns it unchanged otherwise"
61+
(are [input expected] (= expected (sut/maybe-unwrap-quote input))
62+
''{:a 1} {:a 1}
63+
{:b 2} {:b 2}
64+
nil nil
65+
''{} {}
66+
{} {})))

0 commit comments

Comments
 (0)