Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for lein checkouts #517

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,16 @@ a great deal of programming complexity stems from complex interactions
(side effecting events) between things that have local state, it is my
belief that reloadable code is often simply better code.

## Checkouts

Figwheel supports Leiningen's [checkouts][checkouts]
mechanism. This is useful when you want to make changes to a library
while you are developing an application that is using that library.
Figwheel will automatically pick up the :source-paths of any projects
symlinked in the `checkouts` directory. For full details, see
Leiningen's documentation on [checkouts][checkouts].

[checkouts]: (https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md#checkout-dependencies)

## More React Advocacy

Expand Down
1 change: 1 addition & 0 deletions plugin/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

:profiles {:dev {:dependencies [[leiningen "2.7.1"]
[org.clojure/test.check "0.9.0"]]
:resource-paths ["test-resources"]
:source-paths ["dev" "src"]
:aliases {"change-version" ["run" "-m" "figwheel-tasks.core" ":change-version"]
"install-all" ["run" "-m" "figwheel-tasks.core" ":install-all"]}}}
Expand Down
75 changes: 62 additions & 13 deletions plugin/src/leiningen/figwheel.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
(:require
#_[clojure.pprint :as pp]
[leiningen.core.eval :as leval]
[leiningen.core.project :as lproject]
[leiningen.clean :as clean]
[leiningen.core.main :as main]
[clojure.java.io :as io]
[clojure.set :refer [intersection]]
[leiningen.figwheel.fuzzy :as fuz]
[simple-lein-profile-merge.core :as lm]))
[simple-lein-profile-merge.core :as lm])
(:import (java.io File)
(java.nio.file Path)))

(def _figwheel-version_ "0.5.15-SNAPSHOT")

Expand Down Expand Up @@ -284,6 +287,46 @@
(when (every? not-empty args)
(not-empty (apply intersection (map set args)))))

(defn checkout-source-paths
"Get source paths for all of the lein projects in the checkouts directory."
[project]
(let [checkout-project-maps (lproject/read-checkouts project)
parent-project-root-path (.toPath (io/file (:root project)))
checkout-sources (for [co-project checkout-project-maps
source-path-str (:source-paths co-project)
:let [source-path (.toPath (io/file source-path-str))]]
;; Make the checkout source path pretty, e.g. ../utils-lib/src
;; NOTE: this follows the symlink at checkouts/utils-lib
(str (.relativize parent-project-root-path source-path)))]
(distinct checkout-sources)))

(defn map-vals
"Returns a hashmap consisting of the result of applying f to
the value of each set in hashmap.
Function f should accept one single argument."
[f m]
(persistent!
(reduce-kv (fn [m k v] (assoc! m k (f v)))
(transient (empty m)) m)))

(defn update-builds
"Map a function across each cljsbuild build config.

The :cljsbuild :builds path can either be a vector of builds,
or a map of build-ids to builds. This function handles both variants."
[project f]
(if-let [builds (get-in project [:cljsbuild :builds])]
(assoc-in project
[:cljsbuild :builds]
(if (map? builds)
(map-vals f builds)
(map f builds)))
project))

(defn add-source-paths [project source-paths]
(update-builds project
(fn [build] (update build :source-paths (fn [existing-paths] (reduce conj existing-paths source-paths))))))

(defn source-paths-for-classpath [{:keys [figwheel-options all-builds build-ids] :as data}]
(if-not (and all-builds (not-empty all-builds))
[]
Expand Down Expand Up @@ -398,21 +441,27 @@

(defn build-once [project build-ids]
(when-not (report-if-bad-build-ids project build-ids)
(run-build-once
project
(fuzzy-config-from-project project)
(source-paths-for-classpath
(normalize-data project build-ids))
(vec build-ids))))
(let [project (if (:add-checkout-source-paths (figwheel-options project))
(add-source-paths project (checkout-source-paths project))
project)]
(run-build-once
project
(fuzzy-config-from-project project)
(source-paths-for-classpath
(normalize-data project build-ids))
(vec build-ids)))))

(defn figwheel-main [project build-ids]
(when-not (report-if-bad-build-ids project build-ids)
(run-figwheel
project
(fuzzy-config-from-project project)
(source-paths-for-classpath
(normalize-data project build-ids))
(vec build-ids))))
(let [project (if (:add-checkout-source-paths (figwheel-options project))
(add-source-paths project (checkout-source-paths project))
project)]
(run-figwheel
project
(-> project fuzzy-config-from-project)
(source-paths-for-classpath
(normalize-data project build-ids))
(vec build-ids)))))

(defmulti fig-dispatch (fn [command _ _] command))

Expand Down
1 change: 1 addition & 0 deletions plugin/test-resources/test-project-no-checkouts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
11 changes: 11 additions & 0 deletions plugin/test-resources/test-project-no-checkouts/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(defproject test-project-no-checkouts "1.0.0"
:description "A test project for testing Figwheel when there are no checkouts."
:dependencies [[org.clojure/clojure "1.9.0-alpha15"]
[org.clojure/clojurescript "1.9.908"]]
:source-paths ["src"]
:cljsbuild {:builds {:dev {:source-paths ["src"]
:compiler {:main core
:asset-path "js/out"
:output-to "resources/public/js/example.js"
:output-dir "resources/public/js/out"
:optimizations :none}}}})
3 changes: 3 additions & 0 deletions plugin/test-resources/test-project-no-checkouts/src/core.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(ns core)

;; Intentionally left blank
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
11 changes: 11 additions & 0 deletions plugin/test-resources/test-project-with-checkouts/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(defproject test-project-with-checkouts "1.0.0"
:description "A test project for testing Figwheel with checkouts."
:dependencies [[org.clojure/clojure "1.9.0-alpha15"]
[org.clojure/clojurescript "1.9.908"]]
:source-paths ["src"]
:cljsbuild {:builds {:dev {:source-paths ["src"]
:compiler {:main core
:asset-path "js/out"
:output-to "resources/public/js/example.js"
:output-dir "resources/public/js/out"
:optimizations :none}}}})
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(ns core)

;; Intentionally left blank
1 change: 1 addition & 0 deletions plugin/test-resources/utils-lib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
11 changes: 11 additions & 0 deletions plugin/test-resources/utils-lib/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(defproject utils-lib "1.0.0"
:description "A test project to be consumed as a checkouts project."
:dependencies [[org.clojure/clojure "1.9.0-alpha15"]
[org.clojure/clojurescript "1.9.908"]]
:source-paths ["src"]
:cljsbuild {:builds {:dev {:source-paths ["src"]
:compiler {:main utils
:asset-path "js/out"
:output-to "resources/public/js/example.js"
:output-dir "resources/public/js/out"
:optimizations :none}}}})
3 changes: 3 additions & 0 deletions plugin/test-resources/utils-lib/src/utils.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(ns utils)

;; Intentionally left blank
100 changes: 90 additions & 10 deletions plugin/test/leiningen/figwheel_test.clj
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
(ns leiningen.figwheel-test
(:require
[leiningen.figwheel :as f]
[clojure.test :as t :refer [deftest is testing run-tests]]
[clojure.test.check :as tc]
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]
[clojure.test.check.clojure-test :refer [defspec]]))
[leiningen.figwheel :as f]
[clojure.test :as t :refer [deftest is testing run-tests]]
[clojure.test.check :as tc]
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]
[clojure.test.check.clojure-test :refer [defspec]]
[clojure.java.io :as io]))

(def iterations 10)

Expand Down Expand Up @@ -132,7 +133,86 @@
(gen/vector (gen/map (gen/return :source-paths)
(gen/vector gen/string)))))





(deftest update-builds-test
(testing "vector of builds"
(is (= (f/update-builds {:cljsbuild {:builds [{:id "dev"}
{:id "prod"}
{:id "test"}]}}
(fn [build]
(update build :source-paths conj "src")))
{:cljsbuild {:builds [{:id "dev"
:source-paths ["src"]}
{:id "prod"
:source-paths ["src"]}
{:id "test"
:source-paths ["src"]}]}})))
(testing "map of builds"
(is (= (f/update-builds {:cljsbuild {:builds {"dev" {}
"prod" {}
"test" {}}}}
(fn [build]
(update build :source-paths conj "src")))
{:cljsbuild {:builds {"dev" {:source-paths ["src"]}
"prod" {:source-paths ["src"]}
"test" {:source-paths ["src"]}}}})))
(testing "edge cases around no builds"
(testing "vector of builds"
(is (= (f/update-builds {:cljsbuild {:builds []}}
(fn [x]
1))
{:cljsbuild {:builds []}}))
(is (= (f/update-builds {:cljsbuild {}}
(fn [x]
1))
{:cljsbuild {}})))
(testing "map of builds"
(is (= (f/update-builds {:cljsbuild {:builds {}}}
(fn [x]
1))
{:cljsbuild {:builds {}}}))
(is (= (f/update-builds {:cljsbuild {}}
(fn [x]
1))
{:cljsbuild {}})))))

(deftest add-source-paths-test
(testing "vector of builds"
(is (= (f/add-source-paths
{:cljsbuild {:builds [{:id "dev"
:source-paths ["src/cljs" "src/cljc" "dev"]
:resource-paths ["resources"]}
{:id "prod"
:source-paths ["src/cljs" "src/cljc" "prod"]
:resource-paths ["resources"]}]}}
["../util-lib/src"])
{:cljsbuild {:builds [{:id "dev"
:source-paths ["src/cljs" "src/cljc" "dev" "../util-lib/src"]
:resource-paths ["resources"]}
{:id "prod"
:source-paths ["src/cljs" "src/cljc" "prod" "../util-lib/src"]
:resource-paths ["resources"]}]}}))))

(deftest checkout-source-paths-test
(let [cwd (.getCanonicalFile (io/file "."))]
(testing "test project with checkouts"
(is (= (f/checkout-source-paths
{:root (str cwd "/test-resources/test-project-with-checkouts")
:source-paths ["src"]
:cljsbuild {:builds {:dev {:source-paths ["src"]
:compiler {:main 'core
:asset-path "js/out"
:output-to "resources/public/js/example.js"
:output-dir "resources/public/js/out"
:optimizations :none}}}}})
["../utils-lib/src"])))
(testing "test project with no checkouts"
(is (= (f/checkout-source-paths
{:root (str cwd "/test-resources/test-project-with-no-checkouts")
:source-paths ["src"]
:cljsbuild {:builds {:dev {:source-paths ["src"]
:compiler {:main 'core
:asset-path "js/out"
:output-to "resources/public/js/example.js"
:output-dir "resources/public/js/out"
:optimizations :none}}}}})
[])))))