Skip to content

Commit fd51db4

Browse files
committed
Make hot-reloading of deps.edn also watch associated projects
When working on multiple related code bases linked via `:local/root`, this option can be used to track changes to `deps.edn` in all of them.
1 parent fc85b1d commit fd51db4

File tree

7 files changed

+63
-147
lines changed

7 files changed

+63
-147
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
## Added
66

7+
- Support watching multiple `deps.edn` files referenced via `:local/root`
8+
79
## Fixed
810

911
## Changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ versions, and add them to the classpath.
3030
Caveat: we can only *add* to the classpath, any dependencies that were present
3131
when the app started will remain accessible.
3232

33+
You can pass the option `:include-local-roots? true` to also watch any
34+
`deps.edn` of projects that are referenced via `:local/root` in your project's
35+
`deps.edn`
36+
3337
### Classpath inspection and manipulation
3438

3539
```clojure

bb.edn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{:deps
22
{lambdaisland/open-source {:git/url "https://github.com/lambdaisland/open-source"
3-
:sha "a4487e78df935b6d8775019d95292fe4f375bdb7"
3+
:sha "7f39ffb76d47e2ff83d4478957c2ca4fd180f3e5"
44
#_#_:local/root "../open-source"}}}

bin/bb

Lines changed: 0 additions & 125 deletions
This file was deleted.

bin/proj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!bin/bb
1+
#!/usr/bin/env bb
22

33
(ns proj
44
(:require [lioss.main :as lioss]))

deps.edn

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{:paths ["src" "resources"]
22

33
:deps
4-
{rewrite-clj/rewrite-clj {:mvn/version "1.0.682-alpha"}
5-
org.clojure/tools.deps.alpha {:mvn/version "0.12.1030"}
4+
{rewrite-clj/rewrite-clj {:mvn/version "1.1.45"}
5+
org.clojure/tools.deps.alpha {:mvn/version "0.14.1222"}
66
com.lambdaisland/shellutils {:mvn/version "0.0.10"}
77
org.clojure/java.classpath {:mvn/version "1.0.0"}
88
com.nextjournal/beholder {:mvn/version "1.0.0"}}}

src/lambdaisland/classpath/watch_deps.clj

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,45 @@
22
"Watch deps.edn for changes"
33
(:require [clojure.java.classpath :as cp]
44
[clojure.string :as str]
5+
[clojure.java.io :as io]
56
[clojure.tools.deps.alpha :as deps]
67
[lambdaisland.classpath :as licp]
7-
[nextjournal.beholder :as beholder]))
8+
[nextjournal.beholder :as beholder])
9+
(:import java.util.regex.Pattern
10+
java.nio.file.LinkOption
11+
java.nio.file.Paths))
812

913
(def watcher (atom nil))
1014

11-
(defn- on-event [opts {:keys [type path]}]
12-
(when (and (= :modify type)
13-
;; On Mac the path will be absolute and include the watched dir,
14-
;; e.g. `/Users/x/project/./deps.edn`
15-
;; On other systems it seems to be relative, like `./deps.edn`
16-
(str/ends-with? (str path) "./deps.edn"))
17-
(println "✨ Reloading deps.edn ✨")
18-
(let [new-paths (remove (set (map str (cp/system-classpath)))
19-
(:classpath-roots (deps/create-basis opts)))]
20-
(doseq [path new-paths]
21-
(println "- " path))
22-
(licp/install-priority-loader! new-paths))))
15+
(defn canonical-path [p]
16+
(.toRealPath (Paths/get p (into-array String [])) (into-array LinkOption [])))
17+
18+
(def process-root-path (canonical-path "."))
19+
20+
(defn- on-event [deps-path opts {:keys [type path]}]
21+
(locking watcher
22+
(when (and (= :modify type)
23+
;; Before we used "." as the watch path, resulting in a
24+
;; difference between mac, where the path would look like this
25+
;; `/Users/x/project/./deps.edn`, vs Linux where the path would
26+
;; look like this `./deps.edn`.
27+
;;
28+
;; We now turn `"."` into a canonical path before starting the
29+
;; watcher, which means we get fully qualified filenames for both
30+
;; in this equality check.
31+
(= path deps-path))
32+
(try
33+
(println "✨ Reloading"
34+
(str (.relativize process-root-path path))
35+
"")
36+
(let [added-paths (remove (set (map str (cp/system-classpath)))
37+
(:classpath-roots (deps/create-basis opts)))]
38+
(doseq [path added-paths]
39+
(println "+" (str/replace path #"^.*/\.m2/repository/" "")))
40+
(licp/install-priority-loader! added-paths))
41+
(catch Exception e
42+
(println "Error while reloading deps.edn")
43+
(println e))))))
2344

2445
(defn start!
2546
"Start a file system watcher to pick up changes in `deps.edn'
@@ -35,9 +56,20 @@
3556
(swap! watcher
3657
(fn [w]
3758
(when w
38-
(println "Stopping existing `deps.edn' watcher")
39-
(beholder/stop w))
40-
(beholder/watch (partial on-event opts) "."))))
59+
(println "Stopping existing `deps.edn' watchers")
60+
(run! beholder/stop w))
61+
(let [basis (deps/create-basis opts)
62+
roots (cons (str process-root-path)
63+
(when (:include-local-roots? opts)
64+
(->> (vals (:libs basis))
65+
(keep :local/root)
66+
(map canonical-path)
67+
(map str))))]
68+
(doall
69+
(for [root roots]
70+
(beholder/watch
71+
(partial #'on-event (Paths/get root (into-array String ["deps.edn"])) opts)
72+
root)))))))
4173

4274
(defn stop!
4375
"Stop a previously started watcher"
@@ -48,6 +80,9 @@
4880
(beholder/stop w))
4981
nil)))
5082

51-
5283
(comment
53-
(start! {:aliases [:dev]}))
84+
(start! {:aliases [:dev]})
85+
86+
(deps/create-basis {:aliases [:backend]
87+
:extra '{cider/cider-nrepl #:mvn{:version "0.28.5"}
88+
refactor-nrepl/refactor-nrepl #:mvn{:version "3.5.2"}}}))

0 commit comments

Comments
 (0)