clementine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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)))