Skip to content

HTML watcher including browser reloading

Okke Tijhuis edited this page Nov 15, 2015 · 1 revision

This example shows how to add an HTML watcher to your Figwheel start script. It will watch for changes in HTML files and reload the current browser page.

Make sure you're using Figwheel 0.5.0+

Project layout

.
├── resources
│   └── public
│       └── css
├── script
├── src
│   ├── dev
│   │   └── sample
│   └── main
│       └── sample

Start script

For more information on the basics of the following script, read Scripting with component.

To watch for changes in HTML files we'll use the FileSystemWatcher component that is provided by Figwheel. You need to provide a name (for logging purposes), the paths to watch, and a notification handler. The handler is just a function which takes 2 arguments, the watcher and the changed files.

The notification handler will send a message to the connected clients. It will be a custom message with the name :html-files-changed. The names of the changed files are added to the message.

Note that in this example it does not matter which files have changed. It will always reload the current page. Setting it up like this allows the client to perform actions based on the specific file(s) that changed in case you want to.

(require
 '[figwheel-sidecar.components.figwheel-server :as server]
 '[figwheel-sidecar.components.file-system-watcher :as fsw]
 '[figwheel-sidecar.system :as sys]
 '[figwheel-sidecar.utils :as utils]
 '[com.stuartsierra.component :as component])

(def figwheel-config
  {:figwheel-options {}
   :build-ids ["dev"]
   :all-builds
   [{:id "dev"
     :figwheel true
     :source-paths ["src/main" "src/dev"]
     :compiler {:main "sample.dev"
                :asset-path "out"
                :output-to "resources/public/sample.js"
                :output-dir "resources/public/out"
                :verbose true}}]})

(defn make-file [path]
  {:file (utils/remove-root-path path)
   :type :html})

(defn send-files [figwheel-server files]
  (server/send-message figwheel-server
                       ::server/broadcast
                       {:msg-name :html-files-changed
                        :files files}))

(defn handle-notification [watcher files]
  (when-let [changed-files (not-empty (filter #(.endsWith % ".html") (map str files)))]
    (let [figwheel-server (:figwheel-server watcher)
          sendable-files (map #(make-file %) changed-files)]
      (send-files figwheel-server sendable-files)
      (doseq [f sendable-files]
        (println "sending changed HTML file:" (:file f))))))

(def system
  (atom
   (component/system-map
    :figwheel-server (sys/figwheel-system figwheel-config)
    :html-watcher (component/using
                   (fsw/file-system-watcher {:watcher-name "HTML watcher"
                                             :watch-paths ["resources/public"]
                                             :notification-handler handle-notification})
                   [:figwheel-server]))))

(defn start []
  (swap! system component/start))

(defn stop []
  (swap! system component/stop))

(defn reload []
  (stop)
  (start))

(defn repl []
  (sys/cljs-repl (:figwheel-server @system)))

;; Start the components and the repl
(start)
(repl)

Client

You might have noticed that the configuration above uses sample.dev as :main, not sample.core. The reason for this being that we need to add some Figwheel specific client code. The clients need a way to listen for the :html-files-changed messages. This code should only run inside a dev build. Make sure you require your main application namespace.

The client code adds a message watcher with the name :html-watcher. It will receive all messages so we'll need to check if the message name matches :html-files-changed. If it does then the browser page is reloaded.

The script path is assumed to be src/dev/sample/dev.cljs.

(ns sample.dev
  (:require [figwheel.client :as fig]
            [sample.core]))

(fig/add-message-watch
 :html-watcher
 (fn [{:keys [msg-name] :as msg}]
   (when (= msg-name :html-files-changed)
     (.reload js/window.location true)
     (println "Figwheel: HTML file(s) changed. Reloaded page."))))

Assuming the start script is in script/figwheel.clj you can invoke it as follows:

rlwrap lein run -m clojure.main --init script/figwheel.clj -r

The HTML watcher will now monitor the configured :watch-paths. When a change is found a message will be sent to the client and the current page is reloaded.

Note that the provided configuration does NOT start a CSS watcher.