diff --git a/README.md b/README.md index 3feea7f..8380bca 100644 --- a/README.md +++ b/README.md @@ -216,8 +216,10 @@ the Weasel REPL. ## Example -An example project is included in the `weasel-example` subdirectory of -this project. +The `weasel-example` subdirectory has a small, self-contained project with a +step-by-step tutorial that drives a Weasel REPL into both a web page and a Node +process. It uses the Clojure CLI and runs against this repo's source, so it's a +good way to try the current code end to end. ## Need help? diff --git a/weasel-example/.gitignore b/weasel-example/.gitignore index 7d41d0d..0cdb909 100644 --- a/weasel-example/.gitignore +++ b/weasel-example/.gitignore @@ -1,10 +1,7 @@ -pom.xml -*jar -/lib/ -/classes/ /out/ +/out-node/ /target/ -.lein-deps-sum -.lein-repl-history -.lein-plugins/ -/checkouts/ \ No newline at end of file +/.cpcache/ +.nrepl-port +*.jar +pom.xml diff --git a/weasel-example/README.md b/weasel-example/README.md index 922fb63..adba976 100644 --- a/weasel-example/README.md +++ b/weasel-example/README.md @@ -1,22 +1,100 @@ # Weasel example -## Build +A small project that shows a Weasel REPL driving two targets: a web page and a +Node process. It uses the Clojure CLI (`deps.edn`) and depends on the Weasel +checkout next to it (`:local/root ".."`), so it always runs against the source +in this repo. In your own project you'd depend on a released version instead: -Run: +```clojure +{:deps {weasel/weasel {:mvn/version "0.8.0"}}} +``` + +You'll need the [Clojure CLI](https://clojure.org/guides/install_clojure), and +Node for the non-browser part. + +## Browser + +Compile the example: + +```sh +clojure -M:build +``` + +Start a Weasel REPL. The quickest way is a plain terminal REPL, no editor +needed: + +```sh +./scripts/start-repl +``` + +It prints `<< waiting for client to connect ... ` and blocks until the page +connects. In another terminal, serve the project over HTTP and open it: + +```sh +python3 -m http.server 8000 +# now visit http://localhost:8000 +``` + +Serve it over `http://localhost`, not `file://`: a `file://` page has an opaque +origin that the REPL server rejects, and Closure's dev-mode script loading needs +a real HTTP origin anyway. + +Once the page loads it connects back to the REPL, and the terminal drops you at a +`cljs.user=>` prompt running in the browser: + +```clojure +cljs.user=> (js/document.getElementById "afield") +cljs.user=> (set! (.-value (js/document.getElementById "afield")) "hi from the REPL") +cljs.user=> weasel-example.foo/baz +456 +``` -`lein cljsbuild once` +Reload the page and the REPL reconnects on its own - auto-reconnect is on by +default, so you can keep your REPL session across reloads and server restarts. -To fetch the dependencies and build `weasel_example.js`. +### From an editor instead -## REPL +If you'd rather drive it from CIDER, Calva, or another nREPL client, start an +nREPL server with piggieback: + +```sh +clojure -M:repl +``` -Start a `lein repl` and enter this: +Connect your editor to the port it prints, then start the Weasel REPL from the +session: ```clojure (require 'weasel.repl.websocket) -(cider.piggieback/cljs-repl - (weasel.repl.websocket/repl-env :ip "0.0.0.0" :port 9001)) +(cider.piggieback/cljs-repl (weasel.repl.websocket/repl-env :port 9001)) +``` + +(CIDER users can skip the form: `cider-jack-in-cljs` ships a built-in `weasel` +REPL type.) + +## Node + +The same client runs outside the browser. Compile the Node entry point: + +```sh +clojure -M:node-build +``` + +Start a Weasel REPL as above (`./scripts/start-repl`), then run the compiled +client: + +```sh +node out-node/main.js +``` + +It connects back over Node's native `WebSocket` - no browser, no extra +dependencies - and the REPL is now evaluating inside the Node process: + +```clojure +cljs.user=> (.-version js/process) +cljs.user=> (+ 1 2) +3 ``` -Once that executes, open `index.html` in your browser and *voila*, your REPL is connected -to the browser. +That's the whole point of the native-WebSocket client: a REPL-driven workflow in +any modern JavaScript runtime, the browser being just one of them. diff --git a/weasel-example/deps.edn b/weasel-example/deps.edn new file mode 100644 index 0000000..db5e4bf --- /dev/null +++ b/weasel-example/deps.edn @@ -0,0 +1,29 @@ +{:paths ["src"] + + ;; This in-repo example depends on the Weasel checkout next to it, so it always + ;; exercises the current source. In your own project you'd instead pull a + ;; released version, e.g. {weasel/weasel {:mvn/version "0.8.0"}}. + :deps {weasel/weasel {:local/root ".."}} + + :aliases + {;; clojure -M:build compile the browser example into out/ + :build {:main-opts ["-m" "cljs.main" + "-O" "none" + "-d" "out" + "-o" "out/main.js" + "-c" "weasel-example.example"]} + + ;; clojure -M:node-build compile the Node example into out-node/ + :node-build {:main-opts ["-m" "cljs.main" + "-t" "node" + "-O" "none" + "-d" "out-node" + "-o" "out-node/main.js" + "-c" "weasel-example.node"]} + + ;; clojure -M:repl start an nREPL server with piggieback, so you can + ;; connect an editor and piggieback a Weasel REPL onto it + :repl {:extra-deps {nrepl/nrepl {:mvn/version "1.3.1"} + cider/piggieback {:mvn/version "0.6.0"}} + :main-opts ["-m" "nrepl.cmdline" + "--middleware" "[cider.piggieback/wrap-cljs-repl]"]}}} diff --git a/weasel-example/index.html b/weasel-example/index.html index 0f8ef4f..ed1d08a 100644 --- a/weasel-example/index.html +++ b/weasel-example/index.html @@ -1,16 +1,14 @@ - + +
-This page is wired to a Weasel REPL. Open your editor's REPL (or the + terminal REPL) and start evaluating ClojureScript here.
+ + diff --git a/weasel-example/project.clj b/weasel-example/project.clj deleted file mode 100644 index 7b32cb4..0000000 --- a/weasel-example/project.clj +++ /dev/null @@ -1,27 +0,0 @@ -(defproject weasel-example "NOT_RELEASED" - :description "example project for the weasel ClojureScript REPL env" - :url "https://github.com/nrepl/weasel" - :license {:name "Unlicense" - :url "http://unlicense.org/UNLICENSE" - :distribution :repo} - - :dependencies [[org.clojure/clojure "1.12.5"] - [org.clojure/clojurescript "1.12.134"] - [weasel "0.7.1"]] - - :repl-options {:welcome (println "Run (start-weasel) to start a Weasel REPL.") - :init (do - (require 'weasel.repl.websocket) - (defn start-weasel [& opts] - (cider.piggieback/cljs-repl - (apply weasel.repl.websocket/repl-env opts))))} - :source-paths ["src"] - :profiles {:dev {:dependencies [[cider/piggieback "0.6.0"]] - :plugins [[lein-cljsbuild "1.1.8"]] - :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} - :cljsbuild {:builds [{:id "weasel-example" - :source-paths ["src"] - :compiler {:output-to "weasel_example.js" - :output-dir "out" - :optimizations :none - :source-map true}}]}}}) diff --git a/weasel-example/scripts/start-repl b/weasel-example/scripts/start-repl index 35243b2..47f8195 100755 --- a/weasel-example/scripts/start-repl +++ b/weasel-example/scripts/start-repl @@ -1,9 +1,7 @@ #!/bin/sh -CLJSC_CP=$(lein classpath) +# Start a plain terminal Weasel REPL (no editor / nREPL needed). Build the +# browser example first with `clojure -M:build`, then serve index.html over +# http://localhost and open it once this REPL is waiting for a client. -java -server -cp $CLJSC_CP clojure.main -e " -(require '[cljs.repl :as r]) -(require '[weasel.repl.websocket :as w]) -(r/repl (w/repl-env)) -" +exec clojure -M -e "(require '[cljs.repl :as r] '[weasel.repl.websocket :as w]) (r/repl (w/repl-env))" diff --git a/weasel-example/src/weasel_example/example.cljs b/weasel-example/src/weasel_example/example.cljs index dcfdb1f..25d4bfb 100644 --- a/weasel-example/src/weasel_example/example.cljs +++ b/weasel-example/src/weasel_example/example.cljs @@ -1,11 +1,10 @@ (ns weasel-example.example (:require [weasel.repl :as repl] - [weasel-example.foo :as foo :refer [baz]])) + [weasel-example.foo :as foo])) +;; Connect back to the Weasel REPL server. Auto-reconnect is on by default, so +;; this survives page reloads and server restarts on its own. (when-not (repl/alive?) (repl/connect "ws://localhost:9001")) -(if (repl/alive?) - (println "Loaded example")) - -(repl/alive?) +(println "weasel-example loaded; foo/baz =" foo/baz) diff --git a/weasel-example/src/weasel_example/foo.cljs b/weasel-example/src/weasel_example/foo.cljs index c69387f..32790d8 100644 --- a/weasel-example/src/weasel_example/foo.cljs +++ b/weasel-example/src/weasel_example/foo.cljs @@ -1,11 +1,6 @@ (ns weasel-example.foo - [:require [weasel.repl :as repl]]) - -;;; an auxillary namespace that exposes some symbols, for testing -;;; cross-NS analysis at the REPL. + "An auxiliary namespace exposing a couple of vars, to show cross-namespace + analysis working at the REPL.") (def foo 1234) (def baz 456) - -(if (repl/alive?) - (println "Loaded foo")) diff --git a/weasel-example/src/weasel_example/node.cljs b/weasel-example/src/weasel_example/node.cljs new file mode 100644 index 0000000..104fd56 --- /dev/null +++ b/weasel-example/src/weasel_example/node.cljs @@ -0,0 +1,11 @@ +(ns weasel-example.node + "A non-browser entry point. Compiled for Node, this connects back to the same + Weasel REPL server over the platform's native WebSocket - no browser involved. + The open connection keeps the Node process alive, so once you run it you can + evaluate ClojureScript in it from the REPL." + (:require [weasel.repl :as repl] + [weasel-example.foo :as foo])) + +(repl/connect "ws://localhost:9001" :verbose false) + +(println "weasel-example Node client connected; foo/foo =" foo/foo)