clementine 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +4 -2
  3. data/LICENSE.txt +22 -0
  4. data/README.md +11 -6
  5. data/Rakefile +22 -0
  6. data/clementine.gemspec +2 -1
  7. data/ext/clojure-clojurescript-bef56a7/.gitignore +13 -0
  8. data/ext/clojure-clojurescript-bef56a7/Clojurescript.iml +12 -0
  9. data/ext/clojure-clojurescript-bef56a7/README.md +29 -0
  10. data/ext/clojure-clojurescript-bef56a7/benchmark/cljs/benchmark_runner.cljs +155 -0
  11. data/ext/clojure-clojurescript-bef56a7/bin/cljsc +21 -0
  12. data/ext/clojure-clojurescript-bef56a7/bin/cljsc.bat +18 -0
  13. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/bin/cljsc.clj +0 -0
  14. data/ext/clojure-clojurescript-bef56a7/devnotes/README.org +35 -0
  15. data/ext/clojure-clojurescript-bef56a7/devnotes/bcrepl.org +13 -0
  16. data/ext/clojure-clojurescript-bef56a7/devnotes/cljs.org +500 -0
  17. data/ext/clojure-clojurescript-bef56a7/devnotes/corelib.org +583 -0
  18. data/ext/clojure-clojurescript-bef56a7/devnotes/day1.org +203 -0
  19. data/ext/clojure-clojurescript-bef56a7/devnotes/day2.org +44 -0
  20. data/ext/clojure-clojurescript-bef56a7/devnotes/talk.org +126 -0
  21. data/ext/clojure-clojurescript-bef56a7/devnotes/testing +13 -0
  22. data/ext/clojure-clojurescript-bef56a7/devnotes/todo.org +121 -0
  23. data/ext/clojure-clojurescript-bef56a7/epl-v10.html +261 -0
  24. data/ext/clojure-clojurescript-bef56a7/pom.template.xml +88 -0
  25. data/ext/clojure-clojurescript-bef56a7/samples/dom/.gitignore +2 -0
  26. data/ext/clojure-clojurescript-bef56a7/samples/dom/src/dom/test.cljs +48 -0
  27. data/ext/clojure-clojurescript-bef56a7/samples/dom/test.html +30 -0
  28. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/.gitignore +2 -0
  29. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/README.md +53 -0
  30. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/externed-lib.js +7 -0
  31. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/externs.js +3 -0
  32. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-extern.html +14 -0
  33. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-js-dev.html +18 -0
  34. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-js.html +17 -0
  35. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/my-external-lib.js +3 -0
  36. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/src/hello-js/core.cljs +9 -0
  37. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/src/hello-js/extern-example.cljs +5 -0
  38. data/ext/clojure-clojurescript-bef56a7/samples/hello/.gitignore +2 -0
  39. data/ext/clojure-clojurescript-bef56a7/samples/hello/README.md +34 -0
  40. data/ext/clojure-clojurescript-bef56a7/samples/hello/hello-dev.html +18 -0
  41. data/ext/clojure-clojurescript-bef56a7/samples/hello/hello.html +13 -0
  42. data/ext/clojure-clojurescript-bef56a7/samples/hello/src/hello/core.cljs +8 -0
  43. data/ext/clojure-clojurescript-bef56a7/samples/hello/src/hello/foo/bar.cljs +4 -0
  44. data/ext/clojure-clojurescript-bef56a7/samples/nodehello.cljs +18 -0
  45. data/ext/clojure-clojurescript-bef56a7/samples/nodels.cljs +17 -0
  46. data/ext/clojure-clojurescript-bef56a7/samples/repl/.gitignore +2 -0
  47. data/ext/clojure-clojurescript-bef56a7/samples/repl/README.md +101 -0
  48. data/ext/clojure-clojurescript-bef56a7/samples/repl/index.html +27 -0
  49. data/ext/clojure-clojurescript-bef56a7/samples/repl/src/repl/test.cljs +73 -0
  50. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/.gitignore +2 -0
  51. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/README.md +42 -0
  52. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/index-advanced.html +80 -0
  53. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/index.html +88 -0
  54. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/reset.css +48 -0
  55. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/anneal.cljs +66 -0
  56. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/core.cljs +307 -0
  57. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/dom-helpers.cljs +95 -0
  58. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/layout.cljs +100 -0
  59. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/leaderboard.cljs +40 -0
  60. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/radial.cljs +91 -0
  61. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/showgraph.cljs +121 -0
  62. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/timeline.cljs +39 -0
  63. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/style.css +301 -0
  64. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/test_data.txt +1 -0
  65. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/tweet_maps.txt +1 -0
  66. data/ext/clojure-clojurescript-bef56a7/script/benchmark +30 -0
  67. data/ext/clojure-clojurescript-bef56a7/script/bootstrap +70 -0
  68. data/ext/clojure-clojurescript-bef56a7/script/browser-repl +16 -0
  69. data/ext/clojure-clojurescript-bef56a7/script/build +59 -0
  70. data/ext/clojure-clojurescript-bef56a7/script/clean +5 -0
  71. data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/google-closure-library-third-party.pom.template +59 -0
  72. data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/google-closure-library.pom.template +54 -0
  73. data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/make-closure-library-jars.sh +87 -0
  74. data/ext/clojure-clojurescript-bef56a7/script/compile +41 -0
  75. data/ext/clojure-clojurescript-bef56a7/script/repl +13 -0
  76. data/ext/clojure-clojurescript-bef56a7/script/repl.bat +13 -0
  77. data/ext/clojure-clojurescript-bef56a7/script/repljs +15 -0
  78. data/ext/clojure-clojurescript-bef56a7/script/repljs.bat +14 -0
  79. data/ext/clojure-clojurescript-bef56a7/script/test +38 -0
  80. data/ext/clojure-clojurescript-bef56a7/script/test-compile +30 -0
  81. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/analyzer.clj +975 -0
  82. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/closure.clj +173 -73
  83. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/compiler.clj +1081 -0
  84. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/core.clj +1158 -0
  85. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/repl.clj +51 -25
  86. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/browser.clj +258 -0
  87. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/reflect.clj +75 -0
  88. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/repl/rhino.clj +6 -5
  89. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/server.clj +173 -0
  90. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/tagged_literals.clj +30 -0
  91. data/ext/clojure-clojurescript-bef56a7/src/cljs/cljs/core.cljs +7197 -0
  92. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejs.cljs +1 -1
  93. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejs_externs.js +0 -0
  94. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejscli.cljs +1 -1
  95. data/ext/clojure-clojurescript-bef56a7/src/cljs/cljs/reader.cljs +551 -0
  96. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/dom.cljs +59 -13
  97. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/event.cljs +0 -0
  98. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/net.cljs +8 -7
  99. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/repl.cljs +2 -2
  100. data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/core/reducers.cljs +298 -0
  101. data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/data.cljs +162 -0
  102. data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/reflect.cljs +48 -0
  103. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/set.cljs +0 -0
  104. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/string.cljs +4 -10
  105. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/walk.cljs +0 -0
  106. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/zip.cljs +0 -0
  107. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/binding_test.cljs +7 -0
  108. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/binding_test_other_ns.cljs +3 -0
  109. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/core_test.cljs +1678 -0
  110. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/import_test.cljs +11 -0
  111. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/import_test/foo.cljs +5 -0
  112. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/letfn_test.cljs +19 -0
  113. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/macro_test.cljs +6 -0
  114. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/macro_test/macros.clj +5 -0
  115. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test.cljs +14 -0
  116. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test/bar.cljs +3 -0
  117. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test/foo.cljs +7 -0
  118. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/reader_test.cljs +124 -0
  119. data/ext/clojure-clojurescript-bef56a7/test/cljs/clojure/data_test.cljs +22 -0
  120. data/ext/clojure-clojurescript-bef56a7/test/cljs/clojure/string_test.cljs +97 -0
  121. data/ext/clojure-clojurescript-bef56a7/test/cljs/foo/ns_shadow_test.cljs +9 -0
  122. data/ext/clojure-clojurescript-bef56a7/test/cljs/test_runner.cljs +26 -0
  123. data/lib/clementine.rb +3 -24
  124. data/lib/clementine/clojurescript_engine.rb +9 -48
  125. data/lib/clementine/clojurescript_engine/base.rb +15 -0
  126. data/lib/clementine/clojurescript_engine/jruby.rb +46 -0
  127. data/lib/clementine/{clojurescript_engine_mri.rb → clojurescript_engine/mri.rb} +17 -10
  128. data/lib/clementine/version.rb +1 -1
  129. data/test/clojurescript_engine_test.rb +36 -14
  130. metadata +177 -83
  131. data/vendor/assets/lib/clojure.jar +0 -0
  132. data/vendor/assets/lib/compiler.jar +0 -0
  133. data/vendor/assets/lib/goog.jar +0 -0
  134. data/vendor/assets/lib/js.jar +0 -0
  135. data/vendor/assets/src/clj/cljs/compiler.clj +0 -1341
  136. data/vendor/assets/src/clj/cljs/core.clj +0 -702
  137. data/vendor/assets/src/clj/cljs/repl/browser.clj +0 -341
  138. data/vendor/assets/src/cljs/cljs/core.cljs +0 -3330
  139. data/vendor/assets/src/cljs/cljs/reader.cljs +0 -360
@@ -8,9 +8,12 @@
8
8
 
9
9
  (ns cljs.repl
10
10
  (:refer-clojure :exclude [load-file])
11
+ (:import java.io.File)
11
12
  (:require [clojure.string :as string]
12
13
  [clojure.java.io :as io]
13
14
  [cljs.compiler :as comp]
15
+ [cljs.analyzer :as ana]
16
+ [cljs.tagged-literals :as tags]
14
17
  [cljs.closure :as cljsc]))
15
18
 
16
19
  (def ^:dynamic *cljs-verbose* false)
@@ -61,11 +64,17 @@
61
64
  (evaluate-form repl-env env filename form identity))
62
65
  ([repl-env env filename form wrap]
63
66
  (try
64
- (let [ast (comp/analyze env form)
65
- js (comp/emits ast)
66
- wrap-js (comp/emits (comp/analyze env (wrap form)))]
67
+ (let [ast (ana/analyze env form)
68
+ js (comp/emit-str ast)
69
+ wrap-js (comp/emit-str (binding [ana/*cljs-warn-on-undeclared* false
70
+ ana/*cljs-warn-on-redef* false
71
+ ana/*cljs-warn-on-dynamic* false
72
+ ana/*cljs-warn-on-fn-var* false
73
+ ana/*cljs-warn-fn-arity* false]
74
+ (ana/analyze env (wrap form))))]
67
75
  (when (= (:op ast) :ns)
68
- (load-dependencies repl-env (vals (:requires ast))))
76
+ (load-dependencies repl-env (into (vals (:requires ast))
77
+ (distinct (vals (:uses ast))))))
69
78
  (when *cljs-verbose*
70
79
  (print js))
71
80
  (let [ret (-evaluate repl-env filename (:line (meta form)) wrap-js)]
@@ -84,18 +93,18 @@
84
93
 
85
94
  (defn load-stream [repl-env filename stream]
86
95
  (with-open [r (io/reader stream)]
87
- (let [env {:ns (@comp/namespaces comp/*cljs-ns*) :context :statement :locals {}}
96
+ (let [env (ana/empty-env)
88
97
  pbr (clojure.lang.LineNumberingPushbackReader. r)
89
98
  eof (Object.)]
90
99
  (loop [r (read pbr false eof false)]
91
- (let [env (assoc env :ns (@comp/namespaces comp/*cljs-ns*))]
100
+ (let [env (assoc env :ns (ana/get-namespace ana/*cljs-ns*))]
92
101
  (when-not (identical? eof r)
93
102
  (evaluate-form repl-env env filename r)
94
103
  (recur (read pbr false eof false))))))))
95
104
 
96
105
  (defn load-file
97
106
  [repl-env f]
98
- (binding [comp/*cljs-ns* 'cljs.user]
107
+ (binding [ana/*cljs-ns* 'cljs.user]
99
108
  (let [res (if (= \/ (first f)) f (io/resource f))]
100
109
  (assert res (str "Can't find " f " in classpath"))
101
110
  (load-stream repl-env f res))))
@@ -112,7 +121,7 @@
112
121
 
113
122
  (defn- eval-and-print [repl-env env form]
114
123
  (let [ret (evaluate-form repl-env
115
- (assoc env :ns (@comp/namespaces comp/*cljs-ns*))
124
+ (assoc env :ns (ana/get-namespace ana/*cljs-ns*))
116
125
  "<cljs repl>"
117
126
  form
118
127
  (wrap-fn form))]
@@ -123,39 +132,56 @@
123
132
  (prn nil))))))
124
133
 
125
134
  (defn- read-next-form []
126
- (try {:status :success :form (binding [*ns* (create-ns comp/*cljs-ns*)]
135
+ (try {:status :success :form (binding [*ns* (create-ns ana/*cljs-ns*)
136
+ *data-readers* tags/*cljs-data-readers*]
127
137
  (read))}
128
138
  (catch Exception e
129
139
  (println (.getMessage e))
130
140
  {:status :error})))
131
141
 
142
+ (def default-special-fns
143
+ (let [load-file-fn (fn [repl-env file] (load-file repl-env file))]
144
+ {'in-ns (fn [_ quoted-ns]
145
+ (let [ns-name (second quoted-ns)]
146
+ (when-not (ana/get-namespace ns-name)
147
+ (ana/set-namespace ns-name {:name ns-name}))
148
+ (set! ana/*cljs-ns* ns-name)))
149
+ 'load-file load-file-fn
150
+ 'clojure.core/load-file load-file-fn
151
+ 'load-namespace (fn [repl-env ns] (load-namespace repl-env ns))}))
152
+
153
+ (defn analyze-source
154
+ "Given a source directory, analyzes all .cljs files. Used to populate
155
+ cljs.analyzer/namespaces so as to support code reflection."
156
+ [src-dir]
157
+ (if-let [src-dir (and (not (empty? src-dir))
158
+ (File. src-dir))]
159
+ (doseq [file (comp/cljs-files-in src-dir)]
160
+ (ana/analyze-file (str "file://" (.getAbsolutePath file))))))
161
+
132
162
  (defn repl
133
163
  "Note - repl will reload core.cljs every time, even if supplied old repl-env"
134
- [repl-env & {:keys [verbose warn-on-undeclared]}]
164
+ [repl-env & {:keys [verbose warn-on-undeclared special-fns]}]
135
165
  (prn "Type: " :cljs/quit " to quit")
136
- (binding [comp/*cljs-ns* 'cljs.user
166
+ (binding [ana/*cljs-ns* 'cljs.user
137
167
  *cljs-verbose* verbose
138
- comp/*cljs-warn-on-undeclared* warn-on-undeclared]
139
- (let [env {:context :statement :locals {}}]
168
+ ana/*cljs-warn-on-undeclared* warn-on-undeclared]
169
+ (let [env {:context :statement :locals {}}
170
+ special-fns (merge default-special-fns special-fns)
171
+ is-special-fn? (set (keys special-fns))]
140
172
  (-setup repl-env)
141
173
  (loop []
142
- (print (str "ClojureScript:" comp/*cljs-ns* "> "))
174
+ (print (str "ClojureScript:" ana/*cljs-ns* "> "))
143
175
  (flush)
144
176
  (let [{:keys [status form]} (read-next-form)]
145
177
  (cond
146
178
  (= form :cljs/quit) :quit
147
-
179
+
148
180
  (= status :error) (recur)
149
-
150
- (and (seq? form) (= (first form) 'in-ns))
151
- (do (set! comp/*cljs-ns* (second (second form))) (newline) (recur))
152
-
153
- (and (seq? form) ('#{load-file clojure.core/load-file} (first form)))
154
- (do (load-file repl-env (second form)) (newline) (recur))
155
-
156
- (and (seq? form) ('#{load-namespace} (first form)))
157
- (do (load-namespace repl-env (second form)) (newline) (recur))
158
-
181
+
182
+ (and (seq? form) (is-special-fn? (first form)))
183
+ (do (apply (get special-fns (first form)) repl-env (rest form)) (newline) (recur))
184
+
159
185
  :else
160
186
  (do (eval-and-print repl-env env form) (recur)))))
161
187
  (-tear-down repl-env))))
@@ -0,0 +1,258 @@
1
+ ;; Copyright (c) Rich Hickey. All rights reserved.
2
+ ;; The use and distribution terms for this software are covered by the
3
+ ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4
+ ;; which can be found in the file epl-v10.html at the root of this distribution.
5
+ ;; By using this software in any fashion, you are agreeing to be bound by
6
+ ;; the terms of this license.
7
+ ;; You must not remove this notice, or any other, from this software.
8
+
9
+ (ns cljs.repl.browser
10
+ (:refer-clojure :exclude [loaded-libs])
11
+ (:require [clojure.java.io :as io]
12
+ [cljs.compiler :as comp]
13
+ [cljs.closure :as cljsc]
14
+ [cljs.repl :as repl]
15
+ [cljs.repl.server :as server])
16
+ (:import cljs.repl.IJavaScriptEnv))
17
+
18
+ (defonce browser-state (atom {:return-value-fn nil
19
+ :client-js nil}))
20
+
21
+ (def loaded-libs (atom #{}))
22
+ (def preloaded-libs (atom #{}))
23
+
24
+ (defn- set-return-value-fn
25
+ "Save the return value function which will be called when the next
26
+ return value is received."
27
+ [f]
28
+ (swap! browser-state (fn [old] (assoc old :return-value-fn f))))
29
+
30
+ (defn send-for-eval
31
+ "Given a form and a return value function, send the form to the
32
+ browser for evaluation. The return value function will be called
33
+ when the return value is received."
34
+ ([form return-value-fn]
35
+ (send-for-eval @(server/connection) form return-value-fn))
36
+ ([conn form return-value-fn]
37
+ (do (set-return-value-fn return-value-fn)
38
+ (server/send-and-close conn 200 form "text/javascript"))))
39
+
40
+ (defn- return-value
41
+ "Called by the server when a return value is received."
42
+ [val]
43
+ (when-let [f (:return-value-fn @browser-state)]
44
+ (f val)))
45
+
46
+ (defn repl-client-js []
47
+ (slurp @(:client-js @browser-state)))
48
+
49
+ (defn send-repl-client-page
50
+ [request conn opts]
51
+ (server/send-and-close conn 200
52
+ (str "<html><head><meta charset=\"UTF-8\"></head><body>
53
+ <script type=\"text/javascript\">"
54
+ (repl-client-js)
55
+ "</script>"
56
+ "<script type=\"text/javascript\">
57
+ clojure.browser.repl.client.start(\"http://" (-> request :headers :host) "\");
58
+ </script>"
59
+ "</body></html>")
60
+ "text/html"))
61
+
62
+ (defn send-static [{path :path :as request} conn opts]
63
+ (if (and (:static-dir opts)
64
+ (not= "/favicon.ico" path))
65
+ (let [path (if (= "/" path) "/index.html" path)
66
+ st-dir (:static-dir opts)]
67
+ (if-let [local-path (seq (for [x (if (string? st-dir) [st-dir] st-dir)
68
+ :when (.exists (io/file (str x path)))]
69
+ (str x path)))]
70
+ (server/send-and-close conn 200 (slurp (first local-path))
71
+ (condp #(.endsWith %2 %1) path
72
+ ".html" "text/html"
73
+ ".css" "text/css"
74
+ ".html" "text/html"
75
+ ".jpg" "image/jpeg"
76
+ ".js" "text/javascript"
77
+ ".png" "image/png"
78
+ "text/plain"))
79
+ (server/send-404 conn path)))
80
+ (server/send-404 conn path)))
81
+
82
+ (server/dispatch-on :get
83
+ (fn [{:keys [path]} _ _] (.startsWith path "/repl"))
84
+ send-repl-client-page)
85
+
86
+ (server/dispatch-on :get
87
+ (fn [{:keys [path]} _ _] (or (= path "/")
88
+ (.endsWith path ".js")
89
+ (.endsWith path ".html")))
90
+ send-static)
91
+
92
+ (defmulti handle-post (fn [m _ _ ] (:type m)))
93
+
94
+ (server/dispatch-on :post (constantly true) handle-post)
95
+
96
+ (def ordering (agent {:expecting nil :fns {}}))
97
+
98
+ (defmethod handle-post :ready [_ conn _]
99
+ (do (reset! loaded-libs @preloaded-libs)
100
+ (send ordering (fn [_] {:expecting nil :fns {}}))
101
+ (send-for-eval conn
102
+ (cljsc/-compile
103
+ '[(ns cljs.user)
104
+ (set! *print-fn* clojure.browser.repl/repl-print)] {})
105
+ identity)))
106
+
107
+ (defn add-in-order [{:keys [expecting fns]} order f]
108
+ {:expecting (or expecting order) :fns (assoc fns order f)})
109
+
110
+ (defn run-in-order [{:keys [expecting fns]}]
111
+ (loop [order expecting
112
+ fns fns]
113
+ (if-let [f (get fns order)]
114
+ (do (f)
115
+ (recur (inc order) (dissoc fns order)))
116
+ {:expecting order :fns fns})))
117
+
118
+ (defn constrain-order
119
+ "Elements to be printed in the REPL will arrive out of order. Ensure
120
+ that they are printed in the correct order."
121
+ [order f]
122
+ (send-off ordering add-in-order order f)
123
+ (send-off ordering run-in-order))
124
+
125
+ (defmethod handle-post :print [{:keys [content order]} conn _ ]
126
+ (do (constrain-order order (fn [] (do (print (read-string content))
127
+ (.flush *out*))))
128
+ (server/send-and-close conn 200 "ignore__")))
129
+
130
+ (defmethod handle-post :result [{:keys [content order]} conn _ ]
131
+ (constrain-order order (fn [] (do (return-value content)
132
+ (server/set-connection conn)))))
133
+
134
+ (defn browser-eval
135
+ "Given a string of JavaScript, evaluate it in the browser and return a map representing the
136
+ result of the evaluation. The map will contain the keys :type and :value. :type can be
137
+ :success, :exception, or :error. :success means that the JavaScript was evaluated without
138
+ exception and :value will contain the return value of the evaluation. :exception means that
139
+ there was an exception in the browser while evaluating the JavaScript and :value will
140
+ contain the error message. :error means that some other error has occured."
141
+ [form]
142
+ (let [return-value (promise)]
143
+ (send-for-eval form
144
+ (fn [val] (deliver return-value val)))
145
+ (let [ret @return-value]
146
+ (try (read-string ret)
147
+ (catch Exception e
148
+ {:status :error
149
+ :value (str "Could not read return value: " ret)})))))
150
+
151
+ (defn load-javascript
152
+ "Accepts a REPL environment, a list of namespaces, and a URL for a
153
+ JavaScript file which contains the implementation for the list of
154
+ namespaces. Will load the JavaScript file into the REPL environment
155
+ if any of the namespaces have not already been loaded from the
156
+ ClojureScript REPL."
157
+ [repl-env ns-list url]
158
+ (let [missing (remove #(contains? @loaded-libs %) ns-list)]
159
+ (when (seq missing)
160
+ (browser-eval (slurp url))
161
+ (swap! loaded-libs (partial apply conj) missing))))
162
+
163
+ (defrecord BrowserEnv []
164
+ repl/IJavaScriptEnv
165
+ (-setup [this]
166
+ (do (require 'cljs.repl.reflect)
167
+ (repl/analyze-source (:src this))
168
+ (comp/with-core-cljs (server/start this))))
169
+ (-evaluate [_ _ _ js] (browser-eval js))
170
+ (-load [this ns url] (load-javascript this ns url))
171
+ (-tear-down [_]
172
+ (do (server/stop)
173
+ (reset! server/state {})
174
+ (reset! browser-state {}))))
175
+
176
+ (defn compile-client-js [opts]
177
+ (cljsc/build '[(ns clojure.browser.repl.client
178
+ (:require [goog.events :as event]
179
+ [clojure.browser.repl :as repl]))
180
+ (defn start [url]
181
+ (event/listen js/window
182
+ "load"
183
+ (fn []
184
+ (repl/start-evaluator url))))]
185
+ {:optimizations (:optimizations opts)
186
+ :output-dir (:working-dir opts)}))
187
+
188
+ (defn create-client-js-file [opts file-path]
189
+ (let [file (io/file file-path)]
190
+ (when (not (.exists file))
191
+ (spit file (compile-client-js opts)))
192
+ file))
193
+
194
+ (defn- provides-and-requires
195
+ "Return a flat list of all provided and required namespaces from a
196
+ sequence of IJavaScripts."
197
+ [deps]
198
+ (flatten (mapcat (juxt :provides :requires) deps)))
199
+
200
+ (defn- always-preload
201
+ "Return a list of all namespaces which are always loaded into the browser
202
+ when using a browser-connected REPL."
203
+ []
204
+ (let [cljs (provides-and-requires (cljsc/cljs-dependencies {} ["clojure.browser.repl"]))
205
+ goog (provides-and-requires (cljsc/js-dependencies {} cljs))]
206
+ (disj (set (concat cljs goog)) nil)))
207
+
208
+ (defn repl-env
209
+ "Create a browser-connected REPL environment.
210
+
211
+ Options:
212
+
213
+ port: The port on which the REPL server will run. Defaults to 9000.
214
+ working-dir: The directory where the compiled REPL client JavaScript will
215
+ be stored. Defaults to \".repl\".
216
+ serve-static: Should the REPL server attempt to serve static content?
217
+ Defaults to true.
218
+ static-dir: List of directories to search for static content. Defaults to
219
+ [\".\" \"out/\"].
220
+ preloaded-libs: List of namespaces that should not be sent from the REPL server
221
+ to the browser. This may be required if the browser is already
222
+ loading code and reloading it would cause a problem.
223
+ optimizations: The level of optimization to use when compiling the client
224
+ end of the REPL. Defaults to :simple.
225
+ src: The source directory containing user-defined cljs files. Used to
226
+ support reflection. Defaults to \"src/\".
227
+ "
228
+ [& {:as opts}]
229
+ (let [opts (merge (BrowserEnv.)
230
+ {:port 9000
231
+ :optimizations :simple
232
+ :working-dir ".repl"
233
+ :serve-static true
234
+ :static-dir ["." "out/"]
235
+ :preloaded-libs []
236
+ :src "src/"}
237
+ opts)]
238
+ (do (reset! preloaded-libs (set (concat (always-preload) (map str (:preloaded-libs opts)))))
239
+ (reset! loaded-libs @preloaded-libs)
240
+ (swap! browser-state
241
+ (fn [old] (assoc old :client-js
242
+ (future (create-client-js-file
243
+ opts
244
+ (io/file (:working-dir opts) "client.js"))))))
245
+ opts)))
246
+
247
+ (comment
248
+
249
+ (require '[cljs.repl :as repl])
250
+ (require '[cljs.repl.browser :as browser])
251
+ (def env (browser/repl-env))
252
+ (repl/repl env)
253
+ ;; simulate the browser with curl
254
+ ;; curl -v -d "ready" http://127.0.0.1:9000
255
+ ClojureScript:> (+ 1 1)
256
+ ;; curl -v -d "2" http://127.0.0.1:9000
257
+
258
+ )
@@ -0,0 +1,75 @@
1
+ (ns cljs.repl.reflect
2
+ (:refer-clojure :exclude [macroexpand])
3
+ (:require [cljs.repl.server :as server]
4
+ [cljs.analyzer :as analyzer]
5
+ [cljs.compiler :as compiler]
6
+ [clojure.string :as str]
7
+ [clojure.pprint :as pprint]))
8
+
9
+ (defn- dissoc-unless
10
+ "Dissoc all keys from map that do not appear in key-set.
11
+
12
+ (dissoc-unless {:foo 1 :bar 2} #{:foo})
13
+ => {:foo 1}"
14
+ [m key-set]
15
+ {:pre [(map? m)
16
+ (set? key-set)]}
17
+ (reduce (fn [coll key]
18
+ (if (contains? key-set key)
19
+ coll
20
+ (dissoc coll key)))
21
+ m (keys m)))
22
+
23
+ (defn- get-meta [sym]
24
+ (let [ns (symbol (namespace sym))
25
+ n (symbol (name sym))]
26
+ (if-let [sym-meta (get (:defs (get @analyzer/namespaces ns)) n)]
27
+ (-> (dissoc-unless sym-meta
28
+ #{:name :method-params :doc :line :file})
29
+ (update-in [:name] str)
30
+ (update-in [:method-params] #(str (vec %)))))))
31
+
32
+ (defn macroexpand [form]
33
+ "Fully expands a cljs macro form."
34
+ (let [mform (analyzer/macroexpand-1 {} form)]
35
+ (if (identical? form mform)
36
+ mform
37
+ (macroexpand mform))))
38
+
39
+ (defn- url-decode [encoded & [encoding]]
40
+ (java.net.URLDecoder/decode encoded (or encoding "UTF-8")))
41
+
42
+ (def read-url-string (comp read-string url-decode))
43
+
44
+ (defn parse-param
45
+ "Parses the query parameter of a path of the form \"/reflect?var=foo\"
46
+ into the vector [\"var\" \"foo\"]."
47
+ [path]
48
+ (-> (str/split path #"\?")
49
+ (last)
50
+ (str/split #"=")))
51
+
52
+ (defn- compile-and-return
53
+ "Compiles a form to javascript and returns it on conn."
54
+ [conn form]
55
+ (let [ast (analyzer/analyze {:ns {:name 'cljs.user}} form)
56
+ js (try (compiler/emit-str ast)
57
+ (catch Exception e (println e)))]
58
+ (server/send-and-close conn 200 js "text/javascript")))
59
+
60
+ (defmulti handle-reflect-query (fn [[param _] & _] param))
61
+
62
+ (defmethod handle-reflect-query "var"
63
+ [[_ sym] req conn opts]
64
+ (let [sym (read-url-string sym)]
65
+ (compile-and-return conn (get-meta sym))))
66
+
67
+ (defmethod handle-reflect-query "macroform"
68
+ [[_ mform] req conn opts]
69
+ (let [mform (-> mform read-url-string macroexpand)]
70
+ (server/send-and-close conn 200 (with-out-str (pprint/pprint mform)))))
71
+
72
+ (server/dispatch-on :get
73
+ (fn [{:keys [path]} _ _] (.startsWith path "/reflect"))
74
+ (fn [{:keys [path] :as req} conn opts]
75
+ (handle-reflect-query (parse-param path) req conn opts)))