bhauman / lein-figwheel

Figwheel builds your ClojureScript code and hot loads it into the browser as you are coding!
Eclipse Public License 1.0
2.89k stars 209 forks source link

Stable low-level API #530

Open weavejester opened 7 years ago

weavejester commented 7 years ago

Figwheel is a really nice tool, but I found I needed a way of controlling when it reloads. To this end, I built duct-figwheel-component, which is based on Component and more recently duct/server.figwheel, which is a port of the previous library that uses Integrant, instead.

Unfortunately I've had to reach into the guts of figwheel-sidecar to make this work, and this means that every minor Figwheel release results in breakages, most of which are hard to resolve. Rather than continue this route, I was wondering if you'd be open to a stable, low-level API for Figwheel, or just some way of launching Figwheel without an automatic filesystem watcher.

bhauman commented 7 years ago

I'm open to this, can you suggest an API that you need?

I also may be able to steer you towards existing stable functionality that you can use instead.

weavejester commented 7 years ago

Sure 😃

For my needs I essentially want something like this:

(let [server (figwheel-api/start-server options)]
  (figwheel-api/reload-changes server)
  (figwheel-api/stop-server server))

So rather than Figwheel reloading changes automatically, I want to be able to choose when the reloading happens. This is especially useful when I'm developing server and client code simultaneously; when I reset the server, I want to reload the client. If the client is reloading as I'm saving, then it gets out of sync with the server.

Perhaps reload-changes is a little too high-level though. Instead, what about:

(let [server (figwheel-api/start-server options)]
  (figwheel-api/reload-files server ["src/foo.cljs"])
  (figwheel-api/stop-server server))

This would be more low-level, and might be a better basis, since it could hook into a filewatcher like Hawk that returns a list of files, or the user could store a timestamp in an atom and manually check it each time.

So for instance, we could also have a function modified-file-finder that would return a zero-argument function. Each time the function is run, it returns a list of all files modified since it was last executed:

(let [modified-files (figwheel-api/modified-file-finder options)]
  (figwheel-api/reload-files server (modified-files))

This would allow me to build a development environment where I can delay Figwheel reloading until I'm ready.

For people that want this low-level API and file watching, maybe you could also supply start-watch, which takes a callback instead:

(figwheel-api/start-watch options #(figwheel-api/reload-files server %)))

So I think the API in total would look something like:

In this case, options just means the usual figwheel configuration map.

Does that make sense?

arichiardi commented 7 years ago

Nice issue, boot-figreload (@weavejester, it is a boot task that basically sends file-changed msgs over socket to the figwheel client) kind of does/needs the same.

At the moment whenever a file changes, which I detect with the watch task, I try to mimic the set of calls necessary to create the message to send on the websocket.

Had there been a direct "send to socket" function it would have been easier: I have file that changed, relative or absolute path can be defined by the protocol, and the function creates and sends the data correctly for me. Plus it does not matter if the caller is figwheel itself, boot-figreload or weavejester's tooling. Super useful.

Having said that I can help as well on this API because I went down the rabbit hole.