There is a need more complex build scripts for ClojureScript. It would be nice to have a minmal interface and method for composing these scripts. Somehting like:
(-> (watch-cljs ["src"])
(watch-sass [""])
(compile-sass)
(watch-css [""])
(mark-macro-affected-source build-options)
(compile-cljs build-options)
(cljs-browser-repl)
#_(fighweel-browser-connection)
#_(figwheel-browser-repl)
(ambly-repl))It is not lost on me that this is similar to what boot currently does. I have nothing against Boot, it would just be nice to have something that was neither tied to lein or boot that allows us to create and share building tools.
This system would not require buy-in just adhearance to a simple interface.
What should this interface look like?
Right now I'm leaning towards a ring-like functional interace where individual components can use core.async or other methods to handle the nastiness of this type of async build work.
Things to consider.
-
It would be nice to be able to create these composable flows and perhaps explicitely start and stop them.
-
I am also leaning toward a message based flow. Where the current component either recieves and operates on the message eventually forwarding it or passes it on untouched.
Tiny example:
(defn compile-cljs [source-paths build-options]
(fn [f]
(fn [signal]
(f (if (= [:cljs-changed] signal)
(do (cljs.build.api/build source-paths build-options)
[:cljs.build/cljs-compiled {:data ...}])
signal)))))I'm just putting this out there. This idea has been in my head for a year and I'd like to break figwheel down into parts that can be more easily reused.
Obviously I'm a huge fan of this approach, and the idea of a huge trans-tool ecosystem of middlewares is highly intriguing!
My experience with things like this is that the key thing is the value that passes between middlewares. The more context that can be loaded into this value, the more composable the middlewares are. In Ring, the only thing a middleware needs to know to participate in the pipeline and be useful is the shape of the request/response maps.
In Boot1
nilwas threaded through, and the "pipeline" was really just an intricate metaphor for the order that the tasks needed to run in. All the actual coordination happened on the filesystem.That's why in Boot2 we arrived at the
FileSetidea - an immutable value middlewares could accept and return, just like with Ring. This way, in order to be useful and participate, middlewares only needed to know about theFileSetAPI - they didn't need to touch the actual input or output directories in order to function. This eliminates a class of problems related to running tasks in the correct order. It also provides all the context and capabilities middlewares need to cache work between runs, since they can safely retain old FileSets they've seen before. Users can still have problems related to middleware ordering, but at least with a threaded FileSet they are deterministic.Long story short, if there was a single thing I could think of us to coordinate best around, it would be an immutable value type such as the
FileSet. Once we have this, I bet we're off to the races.Thanks for pushing your thoughts out on this! It's a sweet idea.
PS: This blog post about the Broccoli build tool is cool