Full story here.
- Differences from Clojure
:require-macros
- ClojureScript Quick Start
- Using ClojureScript on a Web Page
- Running ClojureScript on Node.js
- Reader Conditionals
.clj
files for Clojure-only logic (and macros!)..cljs
files for ClojureScript-only logic..cljc
files for shared logic (and macros!).#?
reader conditionals syntax.
(defn str->int [s]
#?(:clj (java.lang.Integer/parseInt s)
:cljs (js/parseInt s)))
(ns example.dialogs
#?(:clj (:require [advenjure.dialogs :refer [dialog]])
:cljs (:require-macros [advenjure.dialogs :refer [dialog]])))
- Dot notation same as in Clojure.
js-obj
to create plain key/value objects.clj->js
convert clj structures to js structures.aget
/aset
to access object properties.
(def plain-object (js-obj "a" 1 "b" true "c" nil))
(def nested-object (clj->js :a 1 :b [1 2 3] :c #{"d" true :e nil}))
(aset js/localStorage "key" "value"))
(println (aget js/localStorage "key")))
Translates (more or less) to:
namespace.plain_object = {a: 1, b: true, c: null};
namespace.nested_object = {a: 1, b: [1, 2, 3], c: ["d", true, "e", null]};
window.localStorage.key = "value";
console.log(window.localStorage.key);
(ns advenjure.game)
(loop [state game-state]
(let [input (get-input state)
new-state (process-input state input)]
(if-not (finished? new-state)
(recur new-state)
(exit))))
(ns advenjure.ui.input)
(def input-chan (chan))
(defn process-command
"Callback passed to jQuery terminal upon initialization"
[command]
(go (>! input-chan command)))
(defn get-input
"Wait for input to be written in the input channel"
[state]
(go (<! input-chan)))
(ns advenjure.game)
(go-loop [state game-state]
(let [input (<! (get-input state))
new-state (process-input state input)]
(if-not (finished? new-state)
(recur new-state)
(exit))))
(ns advenjure.async)
(defmacro <!?
"If value is a channel (implements ReadPort protocol), take the value from it
(<!), otherwise return as is. Works with nested channels, I wish there wasn't any.
"
[value]
`(if-cljs
(loop [result# ~value]
(if (satisfies? cljs.core.async.impl.protocols/ReadPort result#)
(recur (cljs.core.async/<! result#))
result#))
~value))
:plugins [[lein-cljsbuild "1.1.4"]]
:cljsbuild
{:builds
{:main {:source-paths ["src"]
:compiler {:output-to "main.js"
:main example.core
:optimizations :simple
:pretty-print false
:optimize-constants true
:static-fns true}}
:dev {:source-paths ["src"]
:compiler {:output-to "dev.js"
:main example.core
:optimizations :none
:source-map true
:pretty-print true}}}}
Compiles with lein cljsbuild once main
:dev {:source-paths ["src"]
:figwheel {:before-jsload "advenjure.ui.input/figwheel-cleanup"}
:compiler {:output-to "resources/public/js/main.js"
:output-dir "resources/public/js/out"
:main example.core
:parallel-build true
:asset-path "js/out"
:optimizations :none
:source-map true
:pretty-print true}}}}
Runs with lein figwheel
How to use third-party js without having the user manually include it in the HTML?
- ClojureScript dependencies
- Need externs so Google Closure doesn't rename external symbols (e.g. "JQuery").
- CLJSJS Project
:foreign-libs
andsrc/deps.cljs
{:foreign-libs
[{:file "jquery/jquery-3.1.1.js"
:file-min "jquery/jquery-3.1.1.min.js"
:provides ["jquery"]}
{:file "jquery.terminal/jquery.terminal-0.11.10.js"
:file-min "jquery.terminal/jquery.terminal-0.11.10.min.js"
:requires ["jquery"]
:provides ["jquery.terminal"]}
{:file "jquery.terminal/jquery.mousewheel.js"
:file-min "jquery.terminal/jquery.mousewheel.min.js"
:requires ["jquery"]
:provides ["jquery.mousewheel"]}
{:file "xregexp/xregexp-all.js"
:file-min "xregexp/xregexp-all.min.js"
:provides ["xregexp"]}]
:externs ["jquery/externs.js" "jquery.terminal/externs.js" "xregexp/externs.js"]}