{"id":13442437,"url":"https://github.com/bhauman/lein-figwheel","last_synced_at":"2025-05-13T19:04:48.852Z","repository":{"id":46281476,"uuid":"18901345","full_name":"bhauman/lein-figwheel","owner":"bhauman","description":"Figwheel builds your ClojureScript code and hot loads it into the browser as you are coding!","archived":false,"fork":false,"pushed_at":"2024-03-26T22:29:38.000Z","size":3739,"stargazers_count":2879,"open_issues_count":98,"forks_count":207,"subscribers_count":66,"default_branch":"master","last_synced_at":"2025-04-19T09:30:07.834Z","etag":null,"topics":["clojure","clojurescript","clojurescript-repl","figwheel","repl"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bhauman.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["bhauman"]}},"created_at":"2014-04-18T03:20:56.000Z","updated_at":"2025-03-31T21:19:49.000Z","dependencies_parsed_at":"2024-05-01T15:27:28.722Z","dependency_job_id":"feda4ad3-39e6-4890-b0b4-6159825151a1","html_url":"https://github.com/bhauman/lein-figwheel","commit_stats":{"total_commits":1724,"total_committers":105,"mean_commits":"16.419047619047618","dds":0.08584686774941996,"last_synced_commit":"70867e90cff4e35d276407b997e8fb14a7f6f07c"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhauman%2Flein-figwheel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhauman%2Flein-figwheel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhauman%2Flein-figwheel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhauman%2Flein-figwheel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bhauman","download_url":"https://codeload.github.com/bhauman/lein-figwheel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250730363,"owners_count":21477753,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","clojurescript","clojurescript-repl","figwheel","repl"],"created_at":"2024-07-31T03:01:45.704Z","updated_at":"2025-04-25T15:51:14.583Z","avatar_url":"https://github.com/bhauman.png","language":"Clojure","funding_links":["https://github.com/sponsors/bhauman","https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=B8B3LKTXKV69C"],"categories":["Clojure","ClojureScript","Framework Agnostic Developer Tools","前端开发框架及项目","tools","Debugging","Awesome ClojureScript"],"sub_categories":["其他_文本生成、文本对话","Development"],"readme":"# lein-figwheel\n\n[![CircleCI](https://circleci.com/gh/bhauman/lein-figwheel.svg?style=svg)](https://circleci.com/gh/bhauman/lein-figwheel)\n\nFigwheel builds your ClojureScript code and hot loads it into the browser as you are coding!\n\n# A new Figwheel!!\n\nThere is a new Figwheel in town!\n\n[Figwheel Main](https://figwheel.org) is a\ncomplete re-write of Figwheel and represents the latest and greatest\nversion of Figwheel. It works great with Leiningen or the new Clojure\nCLI Tools.\n\nSo head over to\n[Figwheel Main](https://github.com/bhauman/figwheel-main) to give it a\ntry.\n\n# lein-figwheel\n\nGet a quick idea of what figwheel does by watching the\n6 minute [flappy bird demo of figwheel](https://www.youtube.com/watch?v=KZjFVdU8VLI).\n\nLearn even more by watching a 45 minute [talk on Figwheel](https://www.youtube.com/watch?v=j-kj2qwJa_E) given at ClojureWest 2015.\n\nRead the [introductory blog post](http://rigsomelight.com/2014/05/01/interactive-programming-flappy-bird-clojurescript.html).\n\n## Support Figwheel\n\nIf Figwheel has fundamentally redefined the way you do front-end work\nplease take a moment and support it:\n\n\u003ca href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=B8B3LKTXKV69C\"\u003e\n\u003cimg src=\"https://s3.amazonaws.com/bhauman-blog-images/Smaller%2BDonate%2BButton%402x.png\" width=\"200\"\u003e\n\u003c/a\u003e\n\nDonated so far: \u0026nbsp;\u0026nbsp;\u0026nbsp; 2015: $73 \u0026nbsp;\u0026nbsp;\u0026nbsp; 2016: $2752 \u0026nbsp;\u0026nbsp;\u0026nbsp; 2017(through October): $1979\n\n#### Current version:\n[![Clojars Project](https://clojars.org/lein-figwheel/latest-version.svg)](https://clojars.org/lein-figwheel)\n\n![Figwheel heads up example](https://s3.amazonaws.com/bhauman-blog-images/figwheel_image.png)\n\n## Features \n\n#### Live code reloading\n\nIf you write [**reloadable\ncode**](https://github.com/bhauman/lein-figwheel#writing-reloadable-code),\nfigwheel can facilitate automated live interactive programming. Every\ntime you save your ClojureScript source file, the changes are sent to\nthe browser so that you can see the effects of modifying your code in\nreal time.\n\n#### Supports Node.js\n\nYou can [use figwheel to live code ClojureScript in Node.js](https://github.com/bhauman/lein-figwheel/wiki/Node.js-development-with-figwheel)!\n\n#### Static file server\n\nThe inclusion of a **static file server** allows you to get a decent\nClojureScript development environment up and running quickly. For\nconvenience there is a `:ring-handler` option so you can load a ring\nhandler into the figwheel server.\n\n#### Live CSS reloading\n\nFigwheel will reload your CSS live as well.\n\n#### Live JavaScript reloading\n\nFigwheel can live reload your JavaScript source files.\n\n#### Heads up display\n\nFigwheel has a non-intrusive heads up display that gives you feedback\non how well your project is compiling. By writing a shell script you\ncan click on files in the heads up display and they will open in your\neditor!\n\n#### Descriptive Errors with Code Context\n\nFigwheel provides descriptive compiler errors that point to where\nthe error is in your code.  These errors appear in the REPL as well\nas the heads up display.\n\n#### First Class Configuration Error Reporting\n\nIt can be quite daunting, when you are configuring a tool for the\nfirst time.  Figwheel currently offers best-of-class configuration\nerror reporting that will help you if you happen to misconfigure\nsomething.\n\n#### Built-in ClojureScript REPL\n\nWhen you launch Figwheel it not only starts a live building/reloading\nprocess but it also optionally launches a CLJS REPL into your running\napplication. This REPL shares compilation information with the\nfigwheel builder, so as you change your code the REPL is also aware of\nthe code changes. The REPL also has some special built-in control\nfunctions that allow you to control the auto-building process and\nexecute various build tasks without having to stop and rerun lein-figwheel.\n\n#### Robust connection\n\nFigwheel's connection is fairly robust. I have experienced figwheel\nsessions that have lasted for days through multiple OS sleeps. You can\nalso use figwheel like a REPL if you are OK with using `print` to output\nthe evaluation results to the browser console.\n\n#### Message broadcast\n\nFigwheel **broadcasts** changes to all connected clients. This means you\ncan see code and CSS changes take place in real time on your phone and\nin your laptop browser simultaneously.\n\n#### Respects dependencies\n\nFigwheel will not load a file that has not been required. It will also\nrespond well to new requirements and dependency tree changes.\n\n#### Calculates minimal reload set\n\nFigwheel does its best to only reload what needs to be reloaded. This\nminimizes the surface area of dynamically reloaded code, which in turn\nshould increase the stability of the client environment.\n\n#### Doesn't load code that is generating warnings\n\nIf your ClojureScript code is generating compiler warnings Figwheel\nwon't load it. This, again, is very helpful in keeping the client\nenvironment stable. This behavior is optional and can be turned off.\n\n## Try Figwheel\n\nMake sure you have the [latest version of leiningen installed](https://github.com/technomancy/leiningen#installation).\n\nYou can try figwheel out quickly with the flappy bird demo:\n\n    git clone https://github.com/bhauman/flappy-bird-demo.git\n\nthen cd into `flappy-bird-demo` and type\n\n    lein figwheel\n\nYou can now goto `localhost:3449/index.html` and open up\n`src/flappy_bird_demo/core.cljs` with your favorite editor and start\ncoding. Make sure you open your browser's development console so you\ncan get feedback about code reloads.\n\nIf you would prefer to greenfield a new project you can use the\nfigwheel leiningen template.\n\n    lein new figwheel hello-world\n\nOr optionally:\n```\n    lein new figwheel hello-world -- --om       ;; for an om based project\n    lein new figwheel hello-world -- --reagent  ;; for a reagent based project \n```\n\n## Learning ClojureScript\n\nIf you are brand new to ClojureScript it is highly recommended that\nyou do the [ClojureScript Quick\nStart](https://clojurescript.org/guides/quick-start)\nfirst. If you skip this you will probably suffer.\n\nThere is a **lot to learn** when you are first learning ClojureScript,\nI recommend that you bite off very small pieces at first. Smaller bites than\nyou would take when learning other languages like JavaScript and Ruby.\n\nPlease don't invest too much time trying to set up a sweet development\nenvironment, there is a diverse set of tools that is constantly in\nflux and it's very difficult to suss out which ones will actually help\nyou. If you spend a lot of time evaluating all these options it can\nbecome very frustrating. If you wait a while, and use simple\ntools you will have much more fun actually using the language itself.\n\n## Quick Start \n\nIf you are new to Figwheel here is a [Quick\nStart](https://github.com/bhauman/lein-figwheel/wiki/Quick-Start) tutorial.\nWorking through this Quick Start will probably save you a tremendous\namount of time.\n\n## Getting Help\n\nYou can get help at both the [ClojureScript Google Group](https://groups.google.com/forum/#!forum/clojurescript)\n\nand on the **#clojurescript**, **#lein-figwheel** and **#beginners** [Clojurians Slack Channels](http://clojurians.net)\n\n## Usage\n\nMake sure you have the [latest version of leiningen installed](https://github.com/technomancy/leiningen#installation).\n\nThen include the following `:dependencies` in your `project.clj` file.\n\n```clojure\n[org.clojure/clojure \"1.9.0\"]\n[org.clojure/clojurescript \"1.10.238\"]\n```\n\nThen include `lein-figwheel` in the `:plugins`\nsection of your project.clj.\n\n```clojure\n[lein-figwheel \"0.5.18\"]\n```\n\n#### Configure your builds\n\nYou also need to have your `:cljsbuild` configuration set up in your\n`project.clj`.\n\nHere is an example:\n\n```clojure\n:cljsbuild {\n  :builds [ { :id \"example\" \n              :source-paths [\"src/\"]\n              :figwheel true\n              :compiler {  :main \"example.core\"\n                           :asset-path \"js/out\"\n                           :output-to \"resources/public/js/example.js\"\n                           :output-dir \"resources/public/js/out\" } } ]\n}\n```\n\nThe important part here is that you have to have at least one `build`\nthat has `:optimizations` set to `:none` or `nil`.\n\nIf you leave out the `:optimizations` key the ClojureScript compiler\nwill default to `:none`.\n\nSetting `:figwheel true` or `:figwheel { :on-jsload \"example.core/reload-hook\" }` will\nautomagically insert the figwheel client code into your application.\nIf you supply `:on-jsload` the name of a function, that function will\nbe called after new code gets reloaded.\n\n**If you want to serve the HTML file that will host your application\nfrom figwheel's built in server**, then the output directory has to be\nin a directory that can be served by the static webserver. The default\nfor the webserver root is \"resources/public\" so your output files need\nto be in a subdirectory of \"resources/public\" unless you change the\nwebserver root. For now the webserver root has to be in a subdirectory\nof `resources`.\n\nIf you are serving your application HTML from your own server you can\nconfigure `:output-to` and `:output-dir` as you like.\n\nStart the figwheel server. (This will get the first `:optimizations`\n`:none` build)\n\n    $ lein figwheel\n\nYou also have the option to specify one or more builds\n\n    $ lein figwheel example\n    $ lein figwheel example example-devcards\n\nThis will start a server at `http://localhost:3449` with your\nresources being served via the compojure `resources` ring handler.\n\nSo you can load the HTML file that's hosting your ClojureScript app\nby going to `http://localhost:3449/\u003cyourfilename\u003e.html`\n\nIf you are using your own server please load your app from that server.\n\n### Figwheel server side configuration\n\nThis is not necessary but you can configure the figwheel system. At\nthe root level of your `project.clj` you can add the following server\nside configuration parameters:\n\n```clojure\n:figwheel {\n   :http-server-root \"public\" ;; this will be in resources/\n   :server-port 5309          ;; default is 3449\n   :server-ip   \"0.0.0.0\"     ;; default is \"localhost\"\n\n   ;; CSS reloading (optional)\n   ;; :css-dirs has no default value \n   ;; if :css-dirs is set figwheel will detect css file changes and\n   ;; send them to the browser\n   :css-dirs [\"resources/public/css\"]\n\n   ;; Server Ring Handler (optional)\n   ;; if you want to embed a ring handler into the figwheel http-kit\n   ;; server\n   :ring-handler example.server/handler\n\n   ;; Clojure Macro reloading\n   ;; disable clj file reloading\n   ; :reload-clj-files false\n   ;; or specify which suffixes will cause the reloading\n   ; :reload-clj-files {:clj true :cljc false}\n\n   ;; To be able to open files in your editor from the heads up display\n   ;; you will need to put a script on your path.\n   ;; that script will have to take a file path, a line number and a column\n   ;; ie. in  ~/bin/myfile-opener\n   ;; #! /bin/sh\n   ;; emacsclient -n +$2:$3 $1 \n   ;;\n   :open-file-command \"myfile-opener\"\n\n   ;; if you want to disable the REPL\n   ;; :repl false\n\n   ;; to configure a different figwheel logfile path\n   ;; :server-logfile \"tmp/logs/figwheel-logfile.log\" \n\n   ;; Start an nREPL server into the running figwheel process\n   ;; :nrepl-port 7888\n\n   ;; Load CIDER, refactor-nrepl and piggieback middleware\n   ;;  :nrepl-middleware [\"cider.nrepl/cider-middleware\"\n   ;;                     \"refactor-nrepl.middleware/wrap-refactor\"\n   ;;                     \"cemerick.piggieback/wrap-cljs-repl\"]\n\n   ;; if you need to watch files with polling instead of FS events\n   ;; :hawk-options {:watcher :polling}     \n   ;; ^ this can be useful in Docker environments\n\n   ;; if your project.clj contains conflicting builds,\n   ;; you can choose to only load the builds specified\n   ;; on the command line\n   ;; :load-all-builds false ; default is true\n} \n```\n\n## Client side usage\n\nMake sure you have setup an html file to host your cljs. For example\nyou can create this `resources/public/index.html` file:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"main-area\"\u003e\n    \u003c/div\u003e\n    \u003cscript src=\"js/example.js\" type=\"text/javascript\"\u003e\u003c/script\u003e   \n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n## CSS Precompilers \n\nUsing SASS or LESS and still want to have the benefits of live CSS reloading?\n\nSimply run your sass or less watcher/compiler on the command line and\nmake sure the final output CSS files land in one of the directories\nthat you have listed in your `:css-dirs` configuration option (mentioned above).\n\nSee [lein-cooper](https://github.com/kouphax/lein-cooper) for a\nfamiliar way to launch processes from lein.\n\n\n## Client side configuration options \n\nInstead of setting `:figwheel true` in your cljsbuild configuration\nyou can pass a map of options as below:\n\n```clojure\n:cljsbuild {\n  :builds [ { :id \"example\" \n              :source-paths [\"src/\"]\n\n              ;; put client config options in :figwheel\n              :figwheel { :websocket-host \"localhost\" \n                          :on-jsload \"example.core/fig-reload\"}\n                          \n              :compiler {  :main \"example.core\"\n                           :asset-path \"js/out\"\n                           :output-to \"resources/public/js/example.js\"\n                           :output-dir \"resources/public/js/out\"\n                           :optimizations :none } } ]\n}\n```\n\nThe following configuration options are available:\n\n```clojure\n\n;; Configure :websocket-host for the figwheel js client to connect to.\n;; (Don't specify the port; figwheel already knows it).\n;; Defaults to \"localhost\".  Valid values are:\n;; \n;;   \u003cany-string\u003e      Uses that exact string as hostname.\n;;\n;;   :js-client-host   Uses window.location.hostname from JS.  This is useful when connecting\n;;                     from a different device/computer on your LAN, e.g. testing mobile\n;;                     safari.\n;;\n;;   :server-ip        Uses the IP address of the figwheel server.  This is Useful in special\n;;                     situations like an iOS (WK)WebView.  Be sure to check your CORS headers.\n;;\n;;   :server-hostname  Like :server-ip, but uses hostname string rather than IP address.\n;;                     (On unix, check that `hostname` outputs the right string in shell).\n;;\n:websocket-host :js-client-host\n\n;; optional callback\n:on-jsload \"example.core/fig-reload\"\n\n;; if you want to do REPL based development and not have\n;; have compiled files autoloaded into the client env\n:autoload false\n\n;; The heads up display is enabled by default; to disable it: \n:heads-up-display false\n\n;; when the compiler emits warnings figwheel blocks the loading of files.\n;; To disable this behavior:\n:load-warninged-code true\n\n;; You can override the websocket url that is used by the figwheel client\n;; by specifying a :websocket-url\n;;\n;; The value of :websocket-url is usually\n;; :websocket-url \"ws://localhost:3449/figwheel-ws\"\n;;\n;; The :websocket-url is normally derived from the :websocket-host option.\n;; If you supply a :websocket-url the :websocket-host option will be ignored.\n;;\n;; The :websocket-url allows you to use tags for common dynamic values.\n;; For example in:\n;; :websocket-url \"ws://[[client-hostname]]:[[server-port]]/figwheel-ws\"\n;; Figwheel will fill in the [[client-hostname]] and [[server-port]] tags\n;;\n;; Available tags are\n;; [[server-hostname]]\n;; [[server-ip]]\n;; [[server-port]]\n;; [[client-hostname]]\n;; [[client-port]]\n```\n\n### More Figwheel Configuration Information\n\nAll Figwheel configuration options are fully specified in \n[sidecar/src/figwheel_sidecar/schemas/config.clj](https://github.com/bhauman/lein-figwheel/blob/master/sidecar/src/figwheel_sidecar/schemas/config.clj). \n\nThis is currently the ultimate configuration reference. (I'm planning on generating \nan official config reference from this file.)\n\n### Preventing and forcing file reloads\n\nFigwheel normally reloads any file that has changed. If you want to\nprevent certain files from being **reloaded** by figwheel, you can add\nmeta-data to the namespace declaration like so:\n\n```clojure\n(ns ^:figwheel-no-load example.core)\n```\n\nFigwheel will not load or reload files that haven't been required by\nyour application. If you want to force a file to be loaded when it\nchanges add the follwoing meta-data the namespace declaration of the file:\n\n```clojure\n(ns ^:figwheel-load example.core)\n```\n\nIt can be very helpful to have a file reload every time a file changes\nin your ClojureScript source tree. This can facilitate reloading your\nmain app and running tests on change.\n\nTo force a file to reload on every change:\n\n```clojure\n(ns ^:figwheel-always example.test-runner)\n```\n\n\n#### Using the ClojureScript REPL\n\nWhen you run `lein figwheel` a REPL will be launched into your application.\n\nYou will need to open your application in a browser in order for the\nREPL to connect and show its prompt.\n\nThis REPL is a little different than other REPLs in that it has live\ncompile information from the build process. This effectively means\nthat you will not have to call `(require` or `(load-namespace` unless\nit is a namespace that isn't in your loaded application's required\ndependencies. In many cases you can just `(in-ns 'my.namespace)` and\neverything you need to access will be there already.\n\nThe REPL get's its syntax highlighting and other features from the\n[rebel-readline](https://github.com/bhauman/rebel-readline) library.\n\nYou can type `:repl/help` to learn more about how to use it.\n\n\u003e For Windows: Rebel-readline will not be automatically included on\n\u003e windows you will have to use `lein trampoline figwheel` in order to\n\u003e get rebel-readline. And this should be done with the knowledge that\n\u003e the classpath may be corrupted if it gets too long and thus things\n\u003e will stop working.\n\u003e See [Scripting Figwheel](#scripting-figwheel) below if you want to use\n\u003e rebel-readline in a stable manner\n\n\n\n##### REPL Figwheel control functions.\n\nThe Figwheel REPL has the following control functions:\n\n```\nFigwheel Controls:\n (stop-autobuild)            ;; stops Figwheel autobuilder\n (start-autobuild [id ...])  ;; starts autobuilder focused on optional ids\n (switch-to-build id ...)    ;; switches autobuilder to different build\n (reset-autobuild)           ;; stops, cleans, and starts autobuilder\n (build-once [id ...])       ;; builds source one time\n (clean-builds [id ..])      ;; deletes compiled cljs target files\n (fig-status)                ;; displays current state of system\n```\n\nThese functions are special functions that poke through the\nClojureScript env into the underlying Clojure process. As such you\ncan't compose them.\n\nYou can think of these functions having an implicit set of build ids\nthat they operate on.\n\nIf you call `(reset-autobuild)` it will stop the figwheel autobuilder,\nclean the builds, reload the build configuration from your\n`project.clj` and then restart the autobuild process.\n\nIf you call `(stop-autobuild)` it will stop the figwheel autobuilder.\n\nIf you call `(start-autobuild)` it will start the figwheel autobuilder\nwith the current implicit build ids.\n\nIf you call `(start-autobuild example)` it will start the figwheel\nautobuilder on the provided build id `example`. It will also make\n`[example]` the implicit set of build ids.\n\n`start-autobuild` and `switch-to-build` are the only functions that\nupdate the build-id set.\n\n`clean-builds` and `build-once` both allow you to do one off builds and\ncleans.  They do not alter the implicit build ids.\n\n`fig-status` displays information on the current Figwheel system state,\nincluding whether the autobuilder is running, which build ids are in\nfocus, and the number of client connections.\n\n## Editor REPLs and nREPL\n\nYou may want a REPL in your editor. This makes it much easier to ship code\nfrom your buffer to be evaluated.\n\n\u003e If you use `lein repl` or something that invokes it like CIDER, you\n\u003e are using nREPL. A ClojureScript REPL will not just run over an nREPL\n\u003e connection without Piggieback.\n\nIf you are just starting out I would use the Figwheel console REPL because it's\naready set up and ready to go, complexity conquered!\n\nIf you want to integrate a REPL into your editor, here are my top\nrecommendations:\n\n**Emacs**:\n* use `inf-clojure` as described on the [wiki page](https://github.com/bhauman/lein-figwheel/wiki/Running-figwheel-with-Emacs-Inferior-Clojure-Interaction-Mode)\n* alternatively use [Cider and nREPL](https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl). *Using the ClojureScript REPL over an nREPL connection is considered advanced*\n\n**Cursive**: use the instructions on the [wiki page](https://github.com/bhauman/lein-figwheel/wiki/Running-figwheel-in-a-Cursive-Clojure-REPL)\n\n**Vi**: use `tmux` mode to interact with the figwheel REPL, still trying to get a wiki page for this if you can help that would be great\n\nIf you are going to use nREPL with Figwheel please see:\n\n[Using Figwheel within NRepl](https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl)\n\n## Scripting Figwheel\n\nAs your development workflow grows in complexity, the declarative\napproach of `lein` can be limiting when you want to launch and control\ndifferent services (ie. SASS compilation). It is really helpful to use\nClojure itself to script whatever workflow services you want.\n\nFigwheel has a Clojure\n[API](https://github.com/bhauman/lein-figwheel/blob/master/sidecar/src/figwheel_sidecar/repl_api.clj)\nthat makes it easy to start, stop and control Figwheel from Clojure.\n\nIn order for the following examples to work, you will need to have\n`[figwheel-sidecar \"0.5.18\"]` and\n`[com.bhauman/rebel-readline \"0.1.4\"]` in your dependencies.\n\nTo start Figwheel from a script, you will need to require the\n`figwheel-sidecar.repl-api` and provide your build configuration to\n`figwheel-sidecar.repl-api/start-figwheel!` like so:\n\n```clojure\n(require '[figwheel-sidecar.repl-api :as ra])\n\n;; this will start figwheel and will start autocompiling the builds specified in `:builds-ids`\n(ra/start-figwheel!\n  {:figwheel-options {} ;; \u003c-- figwheel server config goes here \n   :build-ids [\"dev\"]   ;; \u003c-- a vector of build ids to start autobuilding\n   :all-builds          ;; \u003c-- supply your build configs here\n   [{:id \"dev\"\n     :figwheel true\n     :source-paths [\"src\"]\n     :compiler {:main \"example.core\"\n                :asset-path \"out\"\n                :output-to \"resources/public/main.js\"\n                :output-dir \"resources/public/out\"\n                :verbose true}}]})\n                \n;; you can also just call (ra/start-figwheel!)\n;; and figwheel will do its best to get your config from the\n;; project.clj or a figwheel.edn file\n\n;; start a ClojureScript REPL\n(ra/cljs-repl)\n;; you can optionally supply a build id\n;; (ra/cljs-repl \"dev\")\n```\n\n\u003e  **Build config notes**\n\u003e\n\u003e  It's important to remember that figwheel can autobuild and reload\n\u003e  multiple builds at the same time. It can also switch between builds\n\u003e  and focus on autobuilding one at a time. For this reason you need\n\u003e  to supply the initial `:build-ids` to tell figwheel which builds\n\u003e  you want to start building. It's also really helpful to supply your\n\u003e  `:advanced` builds because while you can't autobuild them you can\n\u003e  call `build-once` on them\n\nAssuming the above script is in `script/figwheel.clj` you can invoke it as follows:\n\n```\n$ lein trampoline run -m clojure.main script/figwheel.clj\n```\n\nThe above command will start figwheel and it will behave just like\nrunning `lein figwheel`.\n\nPlease note that the above command is not running the script in the\nsame environment as `lein repl` or `cider-jack-in`. Both of these\nstart an nREPL session. I am intentionally not using nREPL in order to\nremove a lot of complexity from ClojureScript REPL communication.\n\n\u003e If you are using nREPL, launching the ClojureScript REPL\n\u003e requires that you have Piggieback installed. Please see the section\n\u003e above titled \"Editor REPLs and nREPL\"\n\nLet's make a small helper library and then initialize a Clojure REPL with it:\n\n```clojure\n(require\n '[figwheel-sidecar.repl-api :as ra])\n\n(defn start []\n  (ra/start-figwheel!\n    {:figwheel-options {} ;; \u003c-- figwheel server config goes here \n     :build-ids [\"dev\"]   ;; \u003c-- a vector of build ids to start autobuilding\n     :all-builds          ;; \u003c-- supply your build configs here\n     [{:id \"dev\"\n       :figwheel true\n       :source-paths [\"src\"]\n       :compiler {:main \"example.core\"\n                  :asset-path \"out\"\n                  :output-to \"resources/public/main.js\"\n                  :output-dir \"resources/public/out\"\n                  :verbose true}}]}))\n\n;; Please note that when you stop the Figwheel Server http-kit throws\n;; a java.util.concurrent.RejectedExecutionException, this is expected\n\n(defn stop []\n  (ra/stop-figwheel!))\n\n(defn repl []\n  (ra/cljs-repl))\n```\n\nThe next line will call `clojure.main` and initialize it with our\nscript and then continue on to launch a REPL.\n\n```\n$ lein trampoline run -m clojure.main --init script/figwheel.clj -m rebel-readline.main\n```\n\nAfter the Clojure REPL has launched, you will now have the ability to\ncall `(start)`, `(repl)` and `(stop)` as you need.\n\nYou can also call all of the functions in the [figwheel-sidecar.repl-api](https://github.com/bhauman/lein-figwheel/blob/master/sidecar/src/figwheel_sidecar/repl_api.clj).\n\nThis is a powerful way to work, as you now have the interactivity and\ngenerality of the Clojure programming language available.\n\nNeed to start a server? Go for it.\u003cbr/\u003eNeed to watch and compile SASS files? No problem.\n\n### Tips and Support\n\nFigwheel was created out of the pure desire to make programming more\nfun. While I have been lucky to receive a couple spontaneous donations,\nit is not currently sponsored in any way.\n\nIf you like Figwheel and want to support its development:\n\n\u003ca href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=B8B3LKTXKV69C\"\u003e\n\u003cimg src=\"https://s3.amazonaws.com/bhauman-blog-images/Smaller%2BDonate%2BButton%402x.png\" width=\"200\"\u003e\n\u003c/a\u003e\n\n### Not Magic, just plain old file reloading \n\nThis plugin starts a ClojureScript auto builder, opens a websocket and\nstarts static file server. When you save a ClojureScript file,\nFigwheel will detect that and compile it and other affected files. It\nwill then pass a list of those changed files off to the figwheel\nserver. The figwheel server will in turn push the paths of the\n**relevant** compiled javascript files through a websocket so that the\nbrowser can reload them.\n\nThe main motivation for lein-figwheel is to allow for the interactive\ndevelopment of ClojureScript. Figwheel doesn't provide this out of the\nbox, **the developer has to take care to make their code reloadable**. \n\n## Writing reloadable code\n\nFigwheel relies on having files that can be reloaded. \n\nReloading works beautifully on referentially transparent code and\ncode that only defines behavior without bundling state with the\nbehavior. \n\nIf you are using React or Om it's not hard to write reloadable code,\nin fact you might be doing it already.\n\nThere are several coding patterns to look out for when writing\nreloadable code. \n\nOne problematic pattern is top level definitions that have local\nstate.\n\n```clojure\n(def state (atom {}))\n```\n\nThe `state` definition above is holding an atom that has local state.\nEvery time the file that holds this definition gets reloaded the state\ndefinition will be redefined and the state it holds will be reset back\nto the original state. But with figwheel we are wanting to change our\nprograms while maintaining the state of the running program.\n\nThe way to fix this is to use `defonce`\n\n```clojure\n(defonce state (atom {}))\n```\n\nThis will fix most situations where you have code that is relying on a\ndefinition that has local state. Keep in mind though that if you\nchange the code that is wrapped in a `defonce` you won't see the\nchanges, because the identifier won't be redefined.\n\nComplicated object networks wired together with callbacks (Backbone,\nEmber, etc.) are also problematic. Instantiating these object callback\nnetworks and then storing them in a global var is yet another version\nof this problem.\n\nFunctions that maintain local state like counters and such are also\ndefinitions with local state, and as such are problematic.\n\nYou also need to look out for common setup code that hooks into the browser.\n\nOften you will see statements like this at the bottom of a file.\n\n```clojure\n(.click ($ \"a.button\") (fn [e] (print \"clicked button\")))\n```\n\nEvery time this file gets loaded a new listener will get added to all\nthe anchor tags with a \"button\" class. This is obviously not what we\nwant to happen.\n\nThis code is very problematic and points to the why using the browser\nAPIs directly has always been really difficult. For instance if we make\nit so that these hooks are only executed once, like so:\n\n```clojure\n(defonce setup-stuff \n  (do \n     (.click ($ \"a.button\") (fn [e] (print \"clicked button\")))))\n```\n\nWhen you are live editing code, this doesn't work very well. If you\nalter your HTML template any new \"a.button\" elements aren't going to\nhave the listener bound to them.\n\nYou can fix this by using an event delegation strategy as so:\n\n```clojure  \n(defonce setup-stuff \n  (do \n     (.on ($ \"div#app\") \"click\" \"a.button\" (fn [e] (print \"clicked button\")))))\n```\n\nBut even with the above strategy you won't be able to edit any of the\ncode in the setup up block and see your changes take effect.\n\nIf you are not using React and you want to build things this way and\nhave reloadable code we need to create `setup` and `teardown`\nfunctions to be invoked on code reload.  \n\n```clojure  \n(defn setup []\n   (.on ($ \"div#app\") \"click\" \"a.button\" (fn [e] (print \"clicked button\"))))\n\n(defn teardown []\n   (.off ($ \"div#app\") \"click\" \"a.button\"))\n\n;; define a :on-jsload hook in your :cljsbuild options\n(defn fig-reload-hook []\n      (teardown)\n      (setup))\n\n```\n\nNow you can edit the code in the setup and teardown functions and see\nthe resulting changes in your application.\n\nIn a way you can think of the previous definitions of `setup-stuff` as\nfunctions that have local state of sorts. They are altering and storing\ncallbacks in the DOM directly and this is why it is so problematic.\n\nThis is one of the reasons React is so damn brilliant. You never end\nup storing things directly in the DOM. State is mediated and managed\nfor you. You just describe what should be there and then React takes\ncare of making the appropriate changes. For this reason React is a\nprime candidate for writing reloadable code. React components already\nhave a lifecycle protocol that embeds `setup` and `teardown` in each\ncomponent and invokes them when neccessary.\n\nIt is worth repeating that React components don't have local state, it\njust looks like they do. You have to ask for the local state and React in\nturn looks this state up in a larger state context and returns it,\nvery similar to a State Monad.\n\nReloadable code is easy to write if we are very conscious and careful\nabout the storage of state, state transitions and side effects. Since\na great deal of programming complexity stems from complex interactions\n(side effecting events) between things that have local state, it is my\nbelief that reloadable code is often simply better code.\n\n## License\n\nCopyright © 2018 Bruce Hauman\n\nDistributed under the Eclipse Public License either version 1.0 or any\nlater version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbhauman%2Flein-figwheel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbhauman%2Flein-figwheel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbhauman%2Flein-figwheel/lists"}