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.
- data/.gitignore +1 -0
- data/Gemfile +4 -2
- data/LICENSE.txt +22 -0
- data/README.md +11 -6
- data/Rakefile +22 -0
- data/clementine.gemspec +2 -1
- data/ext/clojure-clojurescript-bef56a7/.gitignore +13 -0
- data/ext/clojure-clojurescript-bef56a7/Clojurescript.iml +12 -0
- data/ext/clojure-clojurescript-bef56a7/README.md +29 -0
- data/ext/clojure-clojurescript-bef56a7/benchmark/cljs/benchmark_runner.cljs +155 -0
- data/ext/clojure-clojurescript-bef56a7/bin/cljsc +21 -0
- data/ext/clojure-clojurescript-bef56a7/bin/cljsc.bat +18 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/bin/cljsc.clj +0 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/README.org +35 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/bcrepl.org +13 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/cljs.org +500 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/corelib.org +583 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/day1.org +203 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/day2.org +44 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/talk.org +126 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/testing +13 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/todo.org +121 -0
- data/ext/clojure-clojurescript-bef56a7/epl-v10.html +261 -0
- data/ext/clojure-clojurescript-bef56a7/pom.template.xml +88 -0
- data/ext/clojure-clojurescript-bef56a7/samples/dom/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/dom/src/dom/test.cljs +48 -0
- data/ext/clojure-clojurescript-bef56a7/samples/dom/test.html +30 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/README.md +53 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/externed-lib.js +7 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/externs.js +3 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-extern.html +14 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-js-dev.html +18 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-js.html +17 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/my-external-lib.js +3 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/src/hello-js/core.cljs +9 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/src/hello-js/extern-example.cljs +5 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/README.md +34 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/hello-dev.html +18 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/hello.html +13 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/src/hello/core.cljs +8 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/src/hello/foo/bar.cljs +4 -0
- data/ext/clojure-clojurescript-bef56a7/samples/nodehello.cljs +18 -0
- data/ext/clojure-clojurescript-bef56a7/samples/nodels.cljs +17 -0
- data/ext/clojure-clojurescript-bef56a7/samples/repl/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/repl/README.md +101 -0
- data/ext/clojure-clojurescript-bef56a7/samples/repl/index.html +27 -0
- data/ext/clojure-clojurescript-bef56a7/samples/repl/src/repl/test.cljs +73 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/README.md +42 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/index-advanced.html +80 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/index.html +88 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/reset.css +48 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/anneal.cljs +66 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/core.cljs +307 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/dom-helpers.cljs +95 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/layout.cljs +100 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/leaderboard.cljs +40 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/radial.cljs +91 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/showgraph.cljs +121 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/timeline.cljs +39 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/style.css +301 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/test_data.txt +1 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/tweet_maps.txt +1 -0
- data/ext/clojure-clojurescript-bef56a7/script/benchmark +30 -0
- data/ext/clojure-clojurescript-bef56a7/script/bootstrap +70 -0
- data/ext/clojure-clojurescript-bef56a7/script/browser-repl +16 -0
- data/ext/clojure-clojurescript-bef56a7/script/build +59 -0
- data/ext/clojure-clojurescript-bef56a7/script/clean +5 -0
- data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/google-closure-library-third-party.pom.template +59 -0
- data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/google-closure-library.pom.template +54 -0
- data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/make-closure-library-jars.sh +87 -0
- data/ext/clojure-clojurescript-bef56a7/script/compile +41 -0
- data/ext/clojure-clojurescript-bef56a7/script/repl +13 -0
- data/ext/clojure-clojurescript-bef56a7/script/repl.bat +13 -0
- data/ext/clojure-clojurescript-bef56a7/script/repljs +15 -0
- data/ext/clojure-clojurescript-bef56a7/script/repljs.bat +14 -0
- data/ext/clojure-clojurescript-bef56a7/script/test +38 -0
- data/ext/clojure-clojurescript-bef56a7/script/test-compile +30 -0
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/analyzer.clj +975 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/closure.clj +173 -73
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/compiler.clj +1081 -0
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/core.clj +1158 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/repl.clj +51 -25
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/browser.clj +258 -0
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/reflect.clj +75 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/repl/rhino.clj +6 -5
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/server.clj +173 -0
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/tagged_literals.clj +30 -0
- data/ext/clojure-clojurescript-bef56a7/src/cljs/cljs/core.cljs +7197 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejs.cljs +1 -1
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejs_externs.js +0 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejscli.cljs +1 -1
- data/ext/clojure-clojurescript-bef56a7/src/cljs/cljs/reader.cljs +551 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/dom.cljs +59 -13
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/event.cljs +0 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/net.cljs +8 -7
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/repl.cljs +2 -2
- data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/core/reducers.cljs +298 -0
- data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/data.cljs +162 -0
- data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/reflect.cljs +48 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/set.cljs +0 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/string.cljs +4 -10
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/walk.cljs +0 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/zip.cljs +0 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/binding_test.cljs +7 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/binding_test_other_ns.cljs +3 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/core_test.cljs +1678 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/import_test.cljs +11 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/import_test/foo.cljs +5 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/letfn_test.cljs +19 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/macro_test.cljs +6 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/macro_test/macros.clj +5 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test.cljs +14 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test/bar.cljs +3 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test/foo.cljs +7 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/reader_test.cljs +124 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/clojure/data_test.cljs +22 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/clojure/string_test.cljs +97 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/foo/ns_shadow_test.cljs +9 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/test_runner.cljs +26 -0
- data/lib/clementine.rb +3 -24
- data/lib/clementine/clojurescript_engine.rb +9 -48
- data/lib/clementine/clojurescript_engine/base.rb +15 -0
- data/lib/clementine/clojurescript_engine/jruby.rb +46 -0
- data/lib/clementine/{clojurescript_engine_mri.rb → clojurescript_engine/mri.rb} +17 -10
- data/lib/clementine/version.rb +1 -1
- data/test/clojurescript_engine_test.rb +36 -14
- metadata +177 -83
- data/vendor/assets/lib/clojure.jar +0 -0
- data/vendor/assets/lib/compiler.jar +0 -0
- data/vendor/assets/lib/goog.jar +0 -0
- data/vendor/assets/lib/js.jar +0 -0
- data/vendor/assets/src/clj/cljs/compiler.clj +0 -1341
- data/vendor/assets/src/clj/cljs/core.clj +0 -702
- data/vendor/assets/src/clj/cljs/repl/browser.clj +0 -341
- data/vendor/assets/src/cljs/cljs/core.cljs +0 -3330
- data/vendor/assets/src/cljs/cljs/reader.cljs +0 -360
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
JavaScript or a deps file for use during development.
|
|
35
35
|
"
|
|
36
36
|
(:require [cljs.compiler :as comp]
|
|
37
|
+
[cljs.analyzer :as ana]
|
|
37
38
|
[clojure.java.io :as io]
|
|
38
39
|
[clojure.string :as string])
|
|
39
40
|
(:import java.io.File
|
|
@@ -94,6 +95,55 @@
|
|
|
94
95
|
(set-options opts compiler-options)
|
|
95
96
|
compiler-options)))
|
|
96
97
|
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
(defn jar-entry-names* [jar-path]
|
|
101
|
+
(with-open [z (java.util.zip.ZipFile. jar-path)]
|
|
102
|
+
(doall (map #(.getName %) (enumeration-seq (.entries z))))))
|
|
103
|
+
|
|
104
|
+
(def jar-entry-names (memoize jar-entry-names*))
|
|
105
|
+
|
|
106
|
+
(defn find-js-jar
|
|
107
|
+
"finds js resources from a given path in a jar file"
|
|
108
|
+
[jar-path lib-path]
|
|
109
|
+
(doall
|
|
110
|
+
(map #(io/resource %)
|
|
111
|
+
(filter #(do
|
|
112
|
+
(and
|
|
113
|
+
(.startsWith % lib-path)
|
|
114
|
+
(.endsWith % ".js")))
|
|
115
|
+
(jar-entry-names jar-path)))))
|
|
116
|
+
(declare to-url)
|
|
117
|
+
(defn find-js-fs
|
|
118
|
+
"finds js resources from a path on the files system"
|
|
119
|
+
[path]
|
|
120
|
+
(let [file (io/file path)]
|
|
121
|
+
(when (.exists file)
|
|
122
|
+
(map to-url (filter #(.endsWith (.getName %) ".js") (file-seq (io/file path)))))))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
(defn find-js-classpath
|
|
126
|
+
"finds all js files on the classpath matching the path provided"
|
|
127
|
+
[path]
|
|
128
|
+
(let [process-entry #(if (.endsWith % ".jar")
|
|
129
|
+
(find-js-jar % path)
|
|
130
|
+
(find-js-fs (str % "/" path)))
|
|
131
|
+
cpath-list (let [sysp (System/getProperty "java.class.path" )]
|
|
132
|
+
(if (.contains sysp ";")
|
|
133
|
+
(string/split sysp #";")
|
|
134
|
+
(string/split sysp #":")))]
|
|
135
|
+
(doall (reduce #(let [p (process-entry %2)]
|
|
136
|
+
(if p (concat %1 p) %1)) [] cpath-list))))
|
|
137
|
+
|
|
138
|
+
(defn find-js-resources [path]
|
|
139
|
+
"finds js resources in a given path on either the file system or
|
|
140
|
+
the classpath"
|
|
141
|
+
(let [file (io/file path)]
|
|
142
|
+
(if (.exists file)
|
|
143
|
+
(find-js-fs path)
|
|
144
|
+
(find-js-classpath path))))
|
|
145
|
+
|
|
146
|
+
|
|
97
147
|
(defn load-externs
|
|
98
148
|
"Externs are JavaScript files which contain empty definitions of
|
|
99
149
|
functions which will be provided by the envorinment. Any function in
|
|
@@ -102,22 +152,24 @@
|
|
|
102
152
|
Options may contain an :externs key with a list of file paths to
|
|
103
153
|
load. The :use-only-custom-externs flag may be used to indicate that
|
|
104
154
|
the default externs should be excluded."
|
|
105
|
-
[{:keys [externs use-only-custom-externs target]}]
|
|
106
|
-
(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
(let [js-sources (-> externs filter-js add-target load-js)
|
|
155
|
+
[{:keys [externs use-only-custom-externs target ups-externs]}]
|
|
156
|
+
(let [filter-cp-js (fn [paths]
|
|
157
|
+
(for [p paths u (find-js-classpath p)] u))
|
|
158
|
+
filter-js (fn [paths]
|
|
159
|
+
(for [p paths u (find-js-resources p)] u))
|
|
160
|
+
add-target (fn [ext]
|
|
161
|
+
(if (= :nodejs target)
|
|
162
|
+
(cons (io/resource "cljs/nodejs_externs.js")
|
|
163
|
+
(or ext []))
|
|
164
|
+
ext))
|
|
165
|
+
load-js (fn [ext]
|
|
166
|
+
(map #(js-source-file (.getFile %) (slurp %)) ext))]
|
|
167
|
+
(let [js-sources (-> externs filter-js add-target load-js)
|
|
168
|
+
ups-sources (-> ups-externs filter-cp-js load-js)
|
|
169
|
+
all-sources (concat js-sources ups-sources)]
|
|
118
170
|
(if use-only-custom-externs
|
|
119
|
-
|
|
120
|
-
(into
|
|
171
|
+
all-sources
|
|
172
|
+
(into all-sources (CommandLineRunner/getDefaultExterns))))))
|
|
121
173
|
|
|
122
174
|
(defn ^com.google.javascript.jscomp.Compiler make-closure-compiler []
|
|
123
175
|
(let [compiler (com.google.javascript.jscomp.Compiler.)]
|
|
@@ -142,7 +194,7 @@
|
|
|
142
194
|
(->> (for [line lines x (string/split line #";")] x)
|
|
143
195
|
(map string/trim)
|
|
144
196
|
(take-while #(not (re-matches #".*=[\s]*function\(.*\)[\s]*[{].*" %)))
|
|
145
|
-
(map #(re-matches #".*goog\.(provide|require)\('(.*)'\)" %))
|
|
197
|
+
(map #(re-matches #".*goog\.(provide|require)\(['\"](.*)['\"]\)" %))
|
|
146
198
|
(remove nil?)
|
|
147
199
|
(map #(drop 1 %))
|
|
148
200
|
(reduce (fn [m ns]
|
|
@@ -241,26 +293,36 @@
|
|
|
241
293
|
state (reduce dependency-order-visit state deps)]
|
|
242
294
|
(assoc state :order (conj (:order state) file))))))
|
|
243
295
|
|
|
296
|
+
(defn- pack-string [s]
|
|
297
|
+
(if (string? s)
|
|
298
|
+
{:provides (-provides s)
|
|
299
|
+
:requires (-requires s)
|
|
300
|
+
:file (str "from_source_" (gensym) ".clj")
|
|
301
|
+
::original s}
|
|
302
|
+
s))
|
|
303
|
+
|
|
304
|
+
(defn- unpack-string [m]
|
|
305
|
+
(or (::original m) m))
|
|
306
|
+
|
|
244
307
|
(defn dependency-order
|
|
245
308
|
"Topologically sort a collection of dependencies."
|
|
246
309
|
[coll]
|
|
247
|
-
(let [state (build-index coll)]
|
|
248
|
-
(
|
|
310
|
+
(let [state (build-index (map pack-string coll))]
|
|
311
|
+
(map unpack-string
|
|
312
|
+
(distinct
|
|
313
|
+
(:order (reduce dependency-order-visit (assoc state :order []) (keys state)))))))
|
|
249
314
|
|
|
250
315
|
;; Compile
|
|
251
316
|
;; =======
|
|
252
317
|
|
|
253
|
-
(defn empty-env []
|
|
254
|
-
{:ns (@comp/namespaces comp/*cljs-ns*) :context :statement :locals {}})
|
|
255
|
-
|
|
256
318
|
(defn compile-form-seq
|
|
257
319
|
"Compile a sequence of forms to a JavaScript source string."
|
|
258
320
|
[forms]
|
|
259
321
|
(comp/with-core-cljs
|
|
260
322
|
(with-out-str
|
|
261
|
-
(binding [
|
|
323
|
+
(binding [ana/*cljs-ns* 'cljs.user]
|
|
262
324
|
(doseq [form forms]
|
|
263
|
-
(comp/emit (
|
|
325
|
+
(comp/emit (ana/analyze (ana/empty-env) form)))))))
|
|
264
326
|
|
|
265
327
|
(defn output-directory [opts]
|
|
266
328
|
(or (:output-dir opts) "out"))
|
|
@@ -296,12 +358,11 @@
|
|
|
296
358
|
|
|
297
359
|
(defn compile-dir
|
|
298
360
|
"Recursively compile all cljs files under the given source
|
|
299
|
-
directory. Return a list of JavaScriptFiles
|
|
361
|
+
directory. Return a list of JavaScriptFiles."
|
|
300
362
|
[^File src-dir opts]
|
|
301
363
|
(let [out-dir (output-directory opts)]
|
|
302
|
-
(
|
|
303
|
-
|
|
304
|
-
(comp/compile-root src-dir out-dir)))))
|
|
364
|
+
(map compiled-file
|
|
365
|
+
(comp/compile-root src-dir out-dir))))
|
|
305
366
|
|
|
306
367
|
(defn path-from-jarfile
|
|
307
368
|
"Given the URL of a file within a jar, return the path of the file
|
|
@@ -391,10 +452,11 @@
|
|
|
391
452
|
"Given a library spec (a map containing the keys :file
|
|
392
453
|
and :provides), returns a map containing :provides, :requires, :file
|
|
393
454
|
and :url"
|
|
394
|
-
[lib-spec]
|
|
395
|
-
(
|
|
396
|
-
|
|
397
|
-
|
|
455
|
+
([lib-spec] (load-foreign-library* lib-spec false))
|
|
456
|
+
([lib-spec cp-only?]
|
|
457
|
+
(let [find-func (if cp-only? io/resource find-url)]
|
|
458
|
+
(merge lib-spec {:foreign true
|
|
459
|
+
:url (find-func (:file lib-spec))}))))
|
|
398
460
|
|
|
399
461
|
(def load-foreign-library (memoize load-foreign-library*))
|
|
400
462
|
|
|
@@ -402,20 +464,25 @@
|
|
|
402
464
|
"Given a path to a JavaScript library, which is a directory
|
|
403
465
|
containing Javascript files, return a list of maps
|
|
404
466
|
containing :provides, :requires, :file and :url."
|
|
405
|
-
[path]
|
|
406
|
-
(
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
467
|
+
([path] (load-library* path false))
|
|
468
|
+
([path cp-only?]
|
|
469
|
+
(let [find-func (if cp-only? find-js-classpath find-js-resources)
|
|
470
|
+
graph-node (fn [u]
|
|
471
|
+
(-> (io/reader u)
|
|
472
|
+
line-seq
|
|
473
|
+
parse-js-ns
|
|
474
|
+
(assoc :url u)))]
|
|
475
|
+
(let [js-sources (find-js-resources path)]
|
|
476
|
+
(filter #(seq (:provides %)) (map graph-node js-sources))))))
|
|
413
477
|
|
|
414
478
|
(def load-library (memoize load-library*))
|
|
415
479
|
|
|
416
|
-
(defn library-dependencies [{:
|
|
480
|
+
(defn library-dependencies [{libs :libs foreign-libs :foreign-libs
|
|
481
|
+
ups-libs :ups-libs ups-flibs :ups-foreign-libs}]
|
|
417
482
|
(concat
|
|
483
|
+
(mapcat #(load-library % true) ups-libs) ;upstream deps
|
|
418
484
|
(mapcat load-library libs)
|
|
485
|
+
(mapcat #(load-foreign-library % true) ups-flibs) ;upstream deps
|
|
419
486
|
(map load-foreign-library foreign-libs)))
|
|
420
487
|
|
|
421
488
|
(comment
|
|
@@ -457,8 +524,8 @@
|
|
|
457
524
|
|
|
458
525
|
(defn js-dependencies
|
|
459
526
|
"Given a sequence of Closure namespace strings, return the list of
|
|
460
|
-
all dependencies
|
|
461
|
-
|
|
527
|
+
all dependencies. The returned list includes all Google and
|
|
528
|
+
third-party library dependencies.
|
|
462
529
|
|
|
463
530
|
Third-party libraries are configured using the :libs option where
|
|
464
531
|
the value is a list of directories containing third-party
|
|
@@ -466,7 +533,7 @@
|
|
|
466
533
|
[opts requires]
|
|
467
534
|
(let [index (js-dependency-index opts)]
|
|
468
535
|
(loop [requires requires
|
|
469
|
-
visited requires
|
|
536
|
+
visited (set requires)
|
|
470
537
|
deps #{}]
|
|
471
538
|
(if (seq requires)
|
|
472
539
|
(let [node (get index (first requires))
|
|
@@ -474,7 +541,7 @@
|
|
|
474
541
|
(recur (into (rest requires) new-req)
|
|
475
542
|
(into visited new-req)
|
|
476
543
|
(conj deps node)))
|
|
477
|
-
(
|
|
544
|
+
(remove nil? deps)))))
|
|
478
545
|
|
|
479
546
|
(comment
|
|
480
547
|
;; find dependencies
|
|
@@ -492,11 +559,10 @@
|
|
|
492
559
|
|
|
493
560
|
(defn cljs-dependencies
|
|
494
561
|
"Given a list of all required namespaces, return a list of
|
|
495
|
-
IJavaScripts which are the cljs dependencies
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
already exist.
|
|
562
|
+
IJavaScripts which are the cljs dependencies. The returned list will
|
|
563
|
+
not only include the explicitly required files but any transitive
|
|
564
|
+
depedencies as well. JavaScript files will be compiled to the
|
|
565
|
+
working directory if they do not already exist.
|
|
500
566
|
|
|
501
567
|
Only load dependencies from the classpath."
|
|
502
568
|
[opts requires]
|
|
@@ -517,7 +583,7 @@
|
|
|
517
583
|
(recur (into (rest required-files) new-req)
|
|
518
584
|
(into visited new-req)
|
|
519
585
|
(conj js-deps js)))
|
|
520
|
-
(
|
|
586
|
+
(remove nil? js-deps))))))
|
|
521
587
|
|
|
522
588
|
(comment
|
|
523
589
|
;; only get cljs deps
|
|
@@ -534,15 +600,17 @@
|
|
|
534
600
|
plus all dependencies in dependency order."
|
|
535
601
|
[opts & inputs]
|
|
536
602
|
(let [requires (mapcat -requires inputs)
|
|
537
|
-
required-cljs (cljs-dependencies opts requires)
|
|
603
|
+
required-cljs (remove (set inputs) (cljs-dependencies opts requires))
|
|
538
604
|
required-js (js-dependencies opts (set (concat (mapcat -requires required-cljs) requires)))]
|
|
539
|
-
(
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
605
|
+
(cons (javascript-file nil (io/resource "goog/base.js") ["goog"] nil)
|
|
606
|
+
(dependency-order
|
|
607
|
+
(concat (map #(-> (javascript-file (:foreign %)
|
|
608
|
+
(or (:url %) (io/resource (:file %)))
|
|
609
|
+
(:provides %)
|
|
610
|
+
(:requires %))
|
|
611
|
+
(assoc :group (:group %))) required-js)
|
|
612
|
+
required-cljs
|
|
613
|
+
inputs)))))
|
|
546
614
|
|
|
547
615
|
(comment
|
|
548
616
|
;; add dependencies to literal js
|
|
@@ -600,6 +668,9 @@
|
|
|
600
668
|
(let [closure-compiler (make-closure-compiler)
|
|
601
669
|
externs (load-externs opts)
|
|
602
670
|
compiler-options (make-options opts)
|
|
671
|
+
sources (if (= :whitespace (:optimizations opts))
|
|
672
|
+
(cons "var CLOSURE_NO_DEPS = true;" sources)
|
|
673
|
+
sources)
|
|
603
674
|
inputs (map #(js-source-file (javascript-name %) %) sources)
|
|
604
675
|
result ^Result (.compile closure-compiler externs inputs compiler-options)]
|
|
605
676
|
(if (.success result)
|
|
@@ -771,31 +842,60 @@
|
|
|
771
842
|
"goog.provide('test');\ngoog.require('cljs.core');\nalert('hello');\n")
|
|
772
843
|
)
|
|
773
844
|
|
|
845
|
+
|
|
846
|
+
(defn get-upstream-deps*
|
|
847
|
+
"returns a merged map containing all upstream dependencies defined by libraries on the classpath"
|
|
848
|
+
[]
|
|
849
|
+
(let [classloader (. (Thread/currentThread) (getContextClassLoader))
|
|
850
|
+
upstream-deps (map #(read-string (slurp %)) (enumeration-seq (. classloader (findResources "deps.cljs"))))]
|
|
851
|
+
(doseq [dep upstream-deps]
|
|
852
|
+
(println (str "Upstream deps.cljs found on classpath. " dep " This is an EXPERIMENTAL FEATURE and is not guarenteed to remain stable in future versions.")))
|
|
853
|
+
(apply merge-with concat upstream-deps)))
|
|
854
|
+
|
|
855
|
+
(def get-upstream-deps (memoize get-upstream-deps*))
|
|
856
|
+
|
|
774
857
|
(defn add-header [{:keys [hashbang target]} js]
|
|
775
858
|
(if (= :nodejs target)
|
|
776
|
-
(str "#!" (or hashbang "/usr/bin/
|
|
859
|
+
(str "#!" (or hashbang "/usr/bin/env node") "\n" js)
|
|
777
860
|
js))
|
|
778
861
|
|
|
862
|
+
(defn add-wrapper [{:keys [output-wrapper] :as opts} js]
|
|
863
|
+
(if output-wrapper
|
|
864
|
+
(str ";(function(){\n" js "\n})();\n")
|
|
865
|
+
js))
|
|
866
|
+
|
|
779
867
|
(defn build
|
|
780
868
|
"Given a source which can be compiled, produce runnable JavaScript."
|
|
781
869
|
[source opts]
|
|
870
|
+
(ana/reset-namespaces!)
|
|
782
871
|
(let [opts (if (= :nodejs (:target opts))
|
|
783
872
|
(merge {:optimizations :simple} opts)
|
|
784
873
|
opts)
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
874
|
+
ups-deps (get-upstream-deps)
|
|
875
|
+
all-opts (assoc opts
|
|
876
|
+
:ups-libs (:libs ups-deps)
|
|
877
|
+
:ups-foreign-libs (:foreign-libs ups-deps)
|
|
878
|
+
:ups-externs (:externs ups-deps))]
|
|
879
|
+
(binding [ana/*cljs-static-fns*
|
|
880
|
+
(or (and (= (opts :optimizations) :advanced))
|
|
881
|
+
(:static-fns opts)
|
|
882
|
+
ana/*cljs-static-fns*)
|
|
883
|
+
ana/*cljs-warn-on-undeclared*
|
|
884
|
+
(true? (opts :warnings))]
|
|
885
|
+
(let [compiled (-compile source all-opts)
|
|
886
|
+
js-sources (concat
|
|
887
|
+
(apply add-dependencies all-opts
|
|
888
|
+
(if (coll? compiled) compiled [compiled]))
|
|
889
|
+
(when (= :nodejs (:target all-opts))
|
|
890
|
+
[(-compile (io/resource "cljs/nodejscli.cljs") all-opts)]))
|
|
891
|
+
optim (:optimizations all-opts)]
|
|
892
|
+
(if (and optim (not= optim :none))
|
|
893
|
+
(->> js-sources
|
|
894
|
+
(apply optimize all-opts)
|
|
895
|
+
(add-header all-opts)
|
|
896
|
+
(add-wrapper all-opts)
|
|
897
|
+
(output-one-file all-opts))
|
|
898
|
+
(apply output-unoptimized all-opts js-sources))))))
|
|
799
899
|
|
|
800
900
|
(comment
|
|
801
901
|
|
|
@@ -0,0 +1,1081 @@
|
|
|
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
|
+
(set! *warn-on-reflection* true)
|
|
10
|
+
|
|
11
|
+
(ns cljs.compiler
|
|
12
|
+
(:refer-clojure :exclude [munge macroexpand-1])
|
|
13
|
+
(:require [clojure.java.io :as io]
|
|
14
|
+
[clojure.string :as string]
|
|
15
|
+
[cljs.tagged-literals :as tags]
|
|
16
|
+
[cljs.analyzer :as ana])
|
|
17
|
+
(:import java.lang.StringBuilder))
|
|
18
|
+
|
|
19
|
+
(declare munge)
|
|
20
|
+
|
|
21
|
+
(def js-reserved
|
|
22
|
+
#{"abstract" "boolean" "break" "byte" "case"
|
|
23
|
+
"catch" "char" "class" "const" "continue"
|
|
24
|
+
"debugger" "default" "delete" "do" "double"
|
|
25
|
+
"else" "enum" "export" "extends" "final"
|
|
26
|
+
"finally" "float" "for" "function" "goto" "if"
|
|
27
|
+
"implements" "import" "in" "instanceof" "int"
|
|
28
|
+
"interface" "let" "long" "native" "new"
|
|
29
|
+
"package" "private" "protected" "public"
|
|
30
|
+
"return" "short" "static" "super" "switch"
|
|
31
|
+
"synchronized" "this" "throw" "throws"
|
|
32
|
+
"transient" "try" "typeof" "var" "void"
|
|
33
|
+
"volatile" "while" "with" "yield" "methods"})
|
|
34
|
+
|
|
35
|
+
(def ^:dynamic *position* nil)
|
|
36
|
+
(def ^:dynamic *emitted-provides* nil)
|
|
37
|
+
(def cljs-reserved-file-names #{"deps.cljs"})
|
|
38
|
+
|
|
39
|
+
(defonce ns-first-segments (atom '#{"cljs" "clojure"}))
|
|
40
|
+
|
|
41
|
+
(defn munge
|
|
42
|
+
([s] (munge s js-reserved))
|
|
43
|
+
([s reserved]
|
|
44
|
+
(if (map? s)
|
|
45
|
+
; Unshadowing
|
|
46
|
+
(let [{:keys [name field] :as info} s
|
|
47
|
+
depth (loop [d 0, {:keys [shadow]} info]
|
|
48
|
+
(cond
|
|
49
|
+
shadow (recur (inc d) shadow)
|
|
50
|
+
(@ns-first-segments (str name)) (inc d)
|
|
51
|
+
:else d))
|
|
52
|
+
name (if field
|
|
53
|
+
(str "self__." name)
|
|
54
|
+
name)]
|
|
55
|
+
(if (zero? depth)
|
|
56
|
+
(munge name reserved)
|
|
57
|
+
(symbol (str (munge name reserved) "__$" depth))))
|
|
58
|
+
; String munging
|
|
59
|
+
(let [ss (string/replace (str s) #"\/(.)" ".$1") ; Division is special
|
|
60
|
+
ss (apply str (map #(if (reserved %) (str % "$") %)
|
|
61
|
+
(string/split ss #"(?<=\.)|(?=\.)")))
|
|
62
|
+
ms (clojure.lang.Compiler/munge ss)]
|
|
63
|
+
(if (symbol? s)
|
|
64
|
+
(symbol ms)
|
|
65
|
+
ms)))))
|
|
66
|
+
|
|
67
|
+
(defn- comma-sep [xs]
|
|
68
|
+
(interpose "," xs))
|
|
69
|
+
|
|
70
|
+
(defn- escape-char [^Character c]
|
|
71
|
+
(let [cp (.hashCode c)]
|
|
72
|
+
(case cp
|
|
73
|
+
; Handle printable escapes before ASCII
|
|
74
|
+
34 "\\\""
|
|
75
|
+
92 "\\\\"
|
|
76
|
+
; Handle non-printable escapes
|
|
77
|
+
8 "\\b"
|
|
78
|
+
12 "\\f"
|
|
79
|
+
10 "\\n"
|
|
80
|
+
13 "\\r"
|
|
81
|
+
9 "\\t"
|
|
82
|
+
(if (< 31 cp 127)
|
|
83
|
+
c ; Print simple ASCII characters
|
|
84
|
+
(format "\\u%04X" cp))))) ; Any other character is Unicode
|
|
85
|
+
|
|
86
|
+
(defn- escape-string [^CharSequence s]
|
|
87
|
+
(let [sb (StringBuilder. (count s))]
|
|
88
|
+
(doseq [c s]
|
|
89
|
+
(.append sb (escape-char c)))
|
|
90
|
+
(.toString sb)))
|
|
91
|
+
|
|
92
|
+
(defn- wrap-in-double-quotes [x]
|
|
93
|
+
(str \" x \"))
|
|
94
|
+
|
|
95
|
+
(defmulti emit :op)
|
|
96
|
+
|
|
97
|
+
(defn emits [& xs]
|
|
98
|
+
(doseq [x xs]
|
|
99
|
+
(cond
|
|
100
|
+
(nil? x) nil
|
|
101
|
+
(map? x) (emit x)
|
|
102
|
+
(seq? x) (apply emits x)
|
|
103
|
+
(fn? x) (x)
|
|
104
|
+
:else (do
|
|
105
|
+
(let [s (print-str x)]
|
|
106
|
+
(when *position*
|
|
107
|
+
(swap! *position* (fn [[line column]]
|
|
108
|
+
[line (+ column (count s))])))
|
|
109
|
+
(print s)))))
|
|
110
|
+
nil)
|
|
111
|
+
|
|
112
|
+
(defn ^String emit-str [expr]
|
|
113
|
+
(with-out-str (emit expr)))
|
|
114
|
+
|
|
115
|
+
(defn emitln [& xs]
|
|
116
|
+
(apply emits xs)
|
|
117
|
+
;; Prints column-aligned line number comments; good test of *position*.
|
|
118
|
+
;(when *position*
|
|
119
|
+
; (let [[line column] @*position*]
|
|
120
|
+
; (print (apply str (concat (repeat (- 120 column) \space) ["// " (inc line)])))))
|
|
121
|
+
(println)
|
|
122
|
+
(when *position*
|
|
123
|
+
(swap! *position* (fn [[line column]]
|
|
124
|
+
[(inc line) 0])))
|
|
125
|
+
nil)
|
|
126
|
+
|
|
127
|
+
(defmulti emit-constant class)
|
|
128
|
+
(defmethod emit-constant nil [x] (emits "null"))
|
|
129
|
+
(defmethod emit-constant Long [x] (emits x))
|
|
130
|
+
(defmethod emit-constant Integer [x] (emits x)) ; reader puts Integers in metadata
|
|
131
|
+
(defmethod emit-constant Double [x] (emits x))
|
|
132
|
+
(defmethod emit-constant String [x]
|
|
133
|
+
(emits (wrap-in-double-quotes (escape-string x))))
|
|
134
|
+
(defmethod emit-constant Boolean [x] (emits (if x "true" "false")))
|
|
135
|
+
(defmethod emit-constant Character [x]
|
|
136
|
+
(emits (wrap-in-double-quotes (escape-char x))))
|
|
137
|
+
|
|
138
|
+
(defmethod emit-constant java.util.regex.Pattern [x]
|
|
139
|
+
(let [[_ flags pattern] (re-find #"^(?:\(\?([idmsux]*)\))?(.*)" (str x))]
|
|
140
|
+
(emits \/ (.replaceAll (re-matcher #"/" pattern) "\\\\/") \/ flags)))
|
|
141
|
+
|
|
142
|
+
(defmethod emit-constant clojure.lang.Keyword [x]
|
|
143
|
+
(emits \" "\\uFDD0" \'
|
|
144
|
+
(if (namespace x)
|
|
145
|
+
(str (namespace x) "/") "")
|
|
146
|
+
(name x)
|
|
147
|
+
\"))
|
|
148
|
+
|
|
149
|
+
(defmethod emit-constant clojure.lang.Symbol [x]
|
|
150
|
+
(emits \" "\\uFDD1" \'
|
|
151
|
+
(if (namespace x)
|
|
152
|
+
(str (namespace x) "/") "")
|
|
153
|
+
(name x)
|
|
154
|
+
\"))
|
|
155
|
+
|
|
156
|
+
(defn- emit-meta-constant [x & body]
|
|
157
|
+
(if (meta x)
|
|
158
|
+
(do
|
|
159
|
+
(emits "cljs.core.with_meta(" body ",")
|
|
160
|
+
(emit-constant (meta x))
|
|
161
|
+
(emits ")"))
|
|
162
|
+
(emits body)))
|
|
163
|
+
|
|
164
|
+
(defmethod emit-constant clojure.lang.PersistentList$EmptyList [x]
|
|
165
|
+
(emit-meta-constant x "cljs.core.List.EMPTY"))
|
|
166
|
+
|
|
167
|
+
(defmethod emit-constant clojure.lang.PersistentList [x]
|
|
168
|
+
(emit-meta-constant x
|
|
169
|
+
(concat ["cljs.core.list("]
|
|
170
|
+
(comma-sep (map #(fn [] (emit-constant %)) x))
|
|
171
|
+
[")"])))
|
|
172
|
+
|
|
173
|
+
(defmethod emit-constant clojure.lang.Cons [x]
|
|
174
|
+
(emit-meta-constant x
|
|
175
|
+
(concat ["cljs.core.list("]
|
|
176
|
+
(comma-sep (map #(fn [] (emit-constant %)) x))
|
|
177
|
+
[")"])))
|
|
178
|
+
|
|
179
|
+
(defmethod emit-constant clojure.lang.IPersistentVector [x]
|
|
180
|
+
(emit-meta-constant x
|
|
181
|
+
(concat ["cljs.core.vec(["]
|
|
182
|
+
(comma-sep (map #(fn [] (emit-constant %)) x))
|
|
183
|
+
["])"])))
|
|
184
|
+
|
|
185
|
+
(defmethod emit-constant clojure.lang.IPersistentMap [x]
|
|
186
|
+
(emit-meta-constant x
|
|
187
|
+
(concat ["cljs.core.hash_map("]
|
|
188
|
+
(comma-sep (map #(fn [] (emit-constant %))
|
|
189
|
+
(apply concat x)))
|
|
190
|
+
[")"])))
|
|
191
|
+
|
|
192
|
+
(defmethod emit-constant clojure.lang.PersistentHashSet [x]
|
|
193
|
+
(emit-meta-constant x
|
|
194
|
+
(concat ["cljs.core.set(["]
|
|
195
|
+
(comma-sep (map #(fn [] (emit-constant %)) x))
|
|
196
|
+
["])"])))
|
|
197
|
+
|
|
198
|
+
(defn emit-block
|
|
199
|
+
[context statements ret]
|
|
200
|
+
(when statements
|
|
201
|
+
(emits statements))
|
|
202
|
+
(emit ret))
|
|
203
|
+
|
|
204
|
+
(defmacro emit-wrap [env & body]
|
|
205
|
+
`(let [env# ~env]
|
|
206
|
+
(when (= :return (:context env#)) (emits "return "))
|
|
207
|
+
~@body
|
|
208
|
+
(when-not (= :expr (:context env#)) (emitln ";"))))
|
|
209
|
+
|
|
210
|
+
(defmethod emit :no-op [m])
|
|
211
|
+
|
|
212
|
+
(defmethod emit :var
|
|
213
|
+
[{:keys [info env] :as arg}]
|
|
214
|
+
(let [n (:name info)
|
|
215
|
+
n (if (= (namespace n) "js")
|
|
216
|
+
(name n)
|
|
217
|
+
info)]
|
|
218
|
+
(emit-wrap env (emits (munge n)))))
|
|
219
|
+
|
|
220
|
+
(defmethod emit :meta
|
|
221
|
+
[{:keys [expr meta env]}]
|
|
222
|
+
(emit-wrap env
|
|
223
|
+
(emits "cljs.core.with_meta(" expr "," meta ")")))
|
|
224
|
+
|
|
225
|
+
(def ^:private array-map-threshold 16)
|
|
226
|
+
(def ^:private obj-map-threshold 32)
|
|
227
|
+
|
|
228
|
+
(defmethod emit :map
|
|
229
|
+
[{:keys [env simple-keys? keys vals]}]
|
|
230
|
+
(emit-wrap env
|
|
231
|
+
(cond
|
|
232
|
+
(zero? (count keys))
|
|
233
|
+
(emits "cljs.core.ObjMap.EMPTY")
|
|
234
|
+
|
|
235
|
+
(and simple-keys? (<= (count keys) obj-map-threshold))
|
|
236
|
+
(emits "cljs.core.ObjMap.fromObject(["
|
|
237
|
+
(comma-sep keys) ; keys
|
|
238
|
+
"],{"
|
|
239
|
+
(comma-sep (map (fn [k v]
|
|
240
|
+
(with-out-str (emit k) (print ":") (emit v)))
|
|
241
|
+
keys vals)) ; js obj
|
|
242
|
+
"})")
|
|
243
|
+
|
|
244
|
+
(<= (count keys) array-map-threshold)
|
|
245
|
+
(emits "cljs.core.PersistentArrayMap.fromArrays(["
|
|
246
|
+
(comma-sep keys)
|
|
247
|
+
"],["
|
|
248
|
+
(comma-sep vals)
|
|
249
|
+
"])")
|
|
250
|
+
|
|
251
|
+
:else
|
|
252
|
+
(emits "cljs.core.PersistentHashMap.fromArrays(["
|
|
253
|
+
(comma-sep keys)
|
|
254
|
+
"],["
|
|
255
|
+
(comma-sep vals)
|
|
256
|
+
"])"))))
|
|
257
|
+
|
|
258
|
+
(defmethod emit :vector
|
|
259
|
+
[{:keys [items env]}]
|
|
260
|
+
(emit-wrap env
|
|
261
|
+
(if (empty? items)
|
|
262
|
+
(emits "cljs.core.PersistentVector.EMPTY")
|
|
263
|
+
(emits "cljs.core.PersistentVector.fromArray(["
|
|
264
|
+
(comma-sep items) "], true)"))))
|
|
265
|
+
|
|
266
|
+
(defmethod emit :set
|
|
267
|
+
[{:keys [items env]}]
|
|
268
|
+
(emit-wrap env
|
|
269
|
+
(if (empty? items)
|
|
270
|
+
(emits "cljs.core.PersistentHashSet.EMPTY")
|
|
271
|
+
(emits "cljs.core.PersistentHashSet.fromArray(["
|
|
272
|
+
(comma-sep items) "])"))))
|
|
273
|
+
|
|
274
|
+
(defmethod emit :constant
|
|
275
|
+
[{:keys [form env]}]
|
|
276
|
+
(when-not (= :statement (:context env))
|
|
277
|
+
(emit-wrap env (emit-constant form))))
|
|
278
|
+
|
|
279
|
+
(defn get-tag [e]
|
|
280
|
+
(or (-> e :tag)
|
|
281
|
+
(-> e :info :tag)))
|
|
282
|
+
|
|
283
|
+
(defn infer-tag [e]
|
|
284
|
+
(if-let [tag (get-tag e)]
|
|
285
|
+
tag
|
|
286
|
+
(case (:op e)
|
|
287
|
+
:let (infer-tag (:ret e))
|
|
288
|
+
:if (let [then-tag (infer-tag (:then e))
|
|
289
|
+
else-tag (infer-tag (:else e))]
|
|
290
|
+
(when (= then-tag else-tag)
|
|
291
|
+
then-tag))
|
|
292
|
+
:constant (case (:form e)
|
|
293
|
+
true 'boolean
|
|
294
|
+
false 'boolean
|
|
295
|
+
nil)
|
|
296
|
+
nil)))
|
|
297
|
+
|
|
298
|
+
(defn safe-test? [e]
|
|
299
|
+
(let [tag (infer-tag e)]
|
|
300
|
+
(or (#{'boolean 'seq} tag)
|
|
301
|
+
(when (= (:op e) :constant)
|
|
302
|
+
(let [form (:form e)]
|
|
303
|
+
(not (or (and (string? form) (= form ""))
|
|
304
|
+
(and (number? form) (zero? form)))))))))
|
|
305
|
+
|
|
306
|
+
(defmethod emit :if
|
|
307
|
+
[{:keys [test then else env unchecked]}]
|
|
308
|
+
(let [context (:context env)
|
|
309
|
+
checked (not (or unchecked (safe-test? test)))]
|
|
310
|
+
(if (= :expr context)
|
|
311
|
+
(emits "(" (when checked "cljs.core.truth_") "(" test ")?" then ":" else ")")
|
|
312
|
+
(do
|
|
313
|
+
(if checked
|
|
314
|
+
(emitln "if(cljs.core.truth_(" test "))")
|
|
315
|
+
(emitln "if(" test ")"))
|
|
316
|
+
(emitln "{" then "} else")
|
|
317
|
+
(emitln "{" else "}")))))
|
|
318
|
+
|
|
319
|
+
(defmethod emit :throw
|
|
320
|
+
[{:keys [throw env]}]
|
|
321
|
+
(if (= :expr (:context env))
|
|
322
|
+
(emits "(function(){throw " throw "})()")
|
|
323
|
+
(emitln "throw " throw ";")))
|
|
324
|
+
|
|
325
|
+
(defn emit-comment
|
|
326
|
+
"Emit a nicely formatted comment string."
|
|
327
|
+
[doc jsdoc]
|
|
328
|
+
(let [docs (when doc [doc])
|
|
329
|
+
docs (if jsdoc (concat docs jsdoc) docs)
|
|
330
|
+
docs (remove nil? docs)]
|
|
331
|
+
(letfn [(print-comment-lines [e] (doseq [next-line (string/split-lines e)]
|
|
332
|
+
(emitln "* " (string/trim next-line))))]
|
|
333
|
+
(when (seq docs)
|
|
334
|
+
(emitln "/**")
|
|
335
|
+
(doseq [e docs]
|
|
336
|
+
(when e
|
|
337
|
+
(print-comment-lines e)))
|
|
338
|
+
(emitln "*/")))))
|
|
339
|
+
|
|
340
|
+
(defmethod emit :def
|
|
341
|
+
[{:keys [name init env doc export]}]
|
|
342
|
+
(when init
|
|
343
|
+
(let [mname (munge name)]
|
|
344
|
+
(emit-comment doc (:jsdoc init))
|
|
345
|
+
(emits mname)
|
|
346
|
+
(emits " = " init)
|
|
347
|
+
(when-not (= :expr (:context env)) (emitln ";"))
|
|
348
|
+
(when export
|
|
349
|
+
(emitln "goog.exportSymbol('" (munge export) "', " mname ");")))))
|
|
350
|
+
|
|
351
|
+
(defn emit-apply-to
|
|
352
|
+
[{:keys [name params env]}]
|
|
353
|
+
(let [arglist (gensym "arglist__")
|
|
354
|
+
delegate-name (str (munge name) "__delegate")
|
|
355
|
+
params (map munge params)]
|
|
356
|
+
(emitln "(function (" arglist "){")
|
|
357
|
+
(doseq [[i param] (map-indexed vector (butlast params))]
|
|
358
|
+
(emits "var " param " = cljs.core.first(")
|
|
359
|
+
(dotimes [_ i] (emits "cljs.core.next("))
|
|
360
|
+
(emits arglist ")")
|
|
361
|
+
(dotimes [_ i] (emits ")"))
|
|
362
|
+
(emitln ";"))
|
|
363
|
+
(if (< 1 (count params))
|
|
364
|
+
(do
|
|
365
|
+
(emits "var " (last params) " = cljs.core.rest(")
|
|
366
|
+
(dotimes [_ (- (count params) 2)] (emits "cljs.core.next("))
|
|
367
|
+
(emits arglist)
|
|
368
|
+
(dotimes [_ (- (count params) 2)] (emits ")"))
|
|
369
|
+
(emitln ");")
|
|
370
|
+
(emitln "return " delegate-name "(" (string/join ", " params) ");"))
|
|
371
|
+
(do
|
|
372
|
+
(emits "var " (last params) " = ")
|
|
373
|
+
(emits "cljs.core.seq(" arglist ");")
|
|
374
|
+
(emitln ";")
|
|
375
|
+
(emitln "return " delegate-name "(" (string/join ", " params) ");")))
|
|
376
|
+
(emits "})")))
|
|
377
|
+
|
|
378
|
+
(defn emit-fn-method
|
|
379
|
+
[{:keys [type name variadic params statements ret env recurs max-fixed-arity]}]
|
|
380
|
+
(emit-wrap env
|
|
381
|
+
(emitln "(function " (munge name) "(" (comma-sep (map munge params)) "){")
|
|
382
|
+
(when type
|
|
383
|
+
(emitln "var self__ = this;"))
|
|
384
|
+
(when recurs (emitln "while(true){"))
|
|
385
|
+
(emit-block :return statements ret)
|
|
386
|
+
(when recurs
|
|
387
|
+
(emitln "break;")
|
|
388
|
+
(emitln "}"))
|
|
389
|
+
(emits "})")))
|
|
390
|
+
|
|
391
|
+
(defn emit-variadic-fn-method
|
|
392
|
+
[{:keys [type name variadic params statements ret env recurs max-fixed-arity] :as f}]
|
|
393
|
+
(emit-wrap env
|
|
394
|
+
(let [name (or name (gensym))
|
|
395
|
+
mname (munge name)
|
|
396
|
+
params (map munge params)
|
|
397
|
+
delegate-name (str mname "__delegate")]
|
|
398
|
+
(emitln "(function() { ")
|
|
399
|
+
(emitln "var " delegate-name " = function (" (comma-sep params) "){")
|
|
400
|
+
(when recurs (emitln "while(true){"))
|
|
401
|
+
(emit-block :return statements ret)
|
|
402
|
+
(when recurs
|
|
403
|
+
(emitln "break;")
|
|
404
|
+
(emitln "}"))
|
|
405
|
+
(emitln "};")
|
|
406
|
+
|
|
407
|
+
(emitln "var " mname " = function (" (comma-sep
|
|
408
|
+
(if variadic
|
|
409
|
+
(concat (butlast params) ['var_args])
|
|
410
|
+
params)) "){")
|
|
411
|
+
(when type
|
|
412
|
+
(emitln "var self__ = this;"))
|
|
413
|
+
(when variadic
|
|
414
|
+
(emitln "var " (last params) " = null;")
|
|
415
|
+
(emitln "if (goog.isDef(var_args)) {")
|
|
416
|
+
(emitln " " (last params) " = cljs.core.array_seq(Array.prototype.slice.call(arguments, " (dec (count params)) "),0);")
|
|
417
|
+
(emitln "} "))
|
|
418
|
+
(emitln "return " delegate-name ".call(" (string/join ", " (cons "this" params)) ");")
|
|
419
|
+
(emitln "};")
|
|
420
|
+
|
|
421
|
+
(emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";")
|
|
422
|
+
(emits mname ".cljs$lang$applyTo = ")
|
|
423
|
+
(emit-apply-to (assoc f :name name))
|
|
424
|
+
(emitln ";")
|
|
425
|
+
(emitln mname ".cljs$lang$arity$variadic = " delegate-name ";")
|
|
426
|
+
(emitln "return " mname ";")
|
|
427
|
+
(emitln "})()"))))
|
|
428
|
+
|
|
429
|
+
(defmethod emit :fn
|
|
430
|
+
[{:keys [name env methods max-fixed-arity variadic recur-frames loop-lets]}]
|
|
431
|
+
;;fn statements get erased, serve no purpose and can pollute scope if named
|
|
432
|
+
(when-not (= :statement (:context env))
|
|
433
|
+
(let [loop-locals (->> (concat (mapcat :params (filter #(and % @(:flag %)) recur-frames))
|
|
434
|
+
(mapcat :params loop-lets))
|
|
435
|
+
(map munge)
|
|
436
|
+
seq)]
|
|
437
|
+
(when loop-locals
|
|
438
|
+
(when (= :return (:context env))
|
|
439
|
+
(emits "return "))
|
|
440
|
+
(emitln "((function (" (comma-sep (map munge loop-locals)) "){")
|
|
441
|
+
(when-not (= :return (:context env))
|
|
442
|
+
(emits "return ")))
|
|
443
|
+
(if (= 1 (count methods))
|
|
444
|
+
(if variadic
|
|
445
|
+
(emit-variadic-fn-method (assoc (first methods) :name name))
|
|
446
|
+
(emit-fn-method (assoc (first methods) :name name)))
|
|
447
|
+
(let [has-name? (and name true)
|
|
448
|
+
name (or name (gensym))
|
|
449
|
+
mname (munge name)
|
|
450
|
+
maxparams (map munge (apply max-key count (map :params methods)))
|
|
451
|
+
mmap (into {}
|
|
452
|
+
(map (fn [method]
|
|
453
|
+
[(munge (symbol (str mname "__" (count (:params method)))))
|
|
454
|
+
method])
|
|
455
|
+
methods))
|
|
456
|
+
ms (sort-by #(-> % second :params count) (seq mmap))]
|
|
457
|
+
(when (= :return (:context env))
|
|
458
|
+
(emits "return "))
|
|
459
|
+
(emitln "(function() {")
|
|
460
|
+
(emitln "var " mname " = null;")
|
|
461
|
+
(doseq [[n meth] ms]
|
|
462
|
+
(emits "var " n " = ")
|
|
463
|
+
(if (:variadic meth)
|
|
464
|
+
(emit-variadic-fn-method meth)
|
|
465
|
+
(emit-fn-method meth))
|
|
466
|
+
(emitln ";"))
|
|
467
|
+
(emitln mname " = function(" (comma-sep (if variadic
|
|
468
|
+
(concat (butlast maxparams) ['var_args])
|
|
469
|
+
maxparams)) "){")
|
|
470
|
+
(when variadic
|
|
471
|
+
(emitln "var " (last maxparams) " = var_args;"))
|
|
472
|
+
(emitln "switch(arguments.length){")
|
|
473
|
+
(doseq [[n meth] ms]
|
|
474
|
+
(if (:variadic meth)
|
|
475
|
+
(do (emitln "default:")
|
|
476
|
+
(emitln "return " n ".cljs$lang$arity$variadic("
|
|
477
|
+
(comma-sep (butlast maxparams))
|
|
478
|
+
(when (> (count maxparams) 1) ", ")
|
|
479
|
+
"cljs.core.array_seq(arguments, " max-fixed-arity "));"))
|
|
480
|
+
(let [pcnt (count (:params meth))]
|
|
481
|
+
(emitln "case " pcnt ":")
|
|
482
|
+
(emitln "return " n ".call(this" (if (zero? pcnt) nil
|
|
483
|
+
(list "," (comma-sep (take pcnt maxparams)))) ");"))))
|
|
484
|
+
(emitln "}")
|
|
485
|
+
(emitln "throw(new Error('Invalid arity: ' + arguments.length));")
|
|
486
|
+
(emitln "};")
|
|
487
|
+
(when variadic
|
|
488
|
+
(emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";")
|
|
489
|
+
(emitln mname ".cljs$lang$applyTo = " (some #(let [[n m] %] (when (:variadic m) n)) ms) ".cljs$lang$applyTo;"))
|
|
490
|
+
(when has-name?
|
|
491
|
+
(doseq [[n meth] ms]
|
|
492
|
+
(let [c (count (:params meth))]
|
|
493
|
+
(if (:variadic meth)
|
|
494
|
+
(emitln mname ".cljs$lang$arity$variadic = " n ".cljs$lang$arity$variadic;")
|
|
495
|
+
(emitln mname ".cljs$lang$arity$" c " = " n ";")))))
|
|
496
|
+
(emitln "return " mname ";")
|
|
497
|
+
(emitln "})()")))
|
|
498
|
+
(when loop-locals
|
|
499
|
+
(emitln ";})(" (comma-sep loop-locals) "))")))))
|
|
500
|
+
|
|
501
|
+
(defmethod emit :do
|
|
502
|
+
[{:keys [statements ret env]}]
|
|
503
|
+
(let [context (:context env)]
|
|
504
|
+
(when (and statements (= :expr context)) (emits "(function (){"))
|
|
505
|
+
;(when statements (emitln "{"))
|
|
506
|
+
(emit-block context statements ret)
|
|
507
|
+
;(when statements (emits "}"))
|
|
508
|
+
(when (and statements (= :expr context)) (emits "})()"))))
|
|
509
|
+
|
|
510
|
+
(defmethod emit :try*
|
|
511
|
+
[{:keys [env try catch name finally]}]
|
|
512
|
+
(let [context (:context env)
|
|
513
|
+
subcontext (if (= :expr context) :return context)]
|
|
514
|
+
(if (or name finally)
|
|
515
|
+
(do
|
|
516
|
+
(when (= :expr context) (emits "(function (){"))
|
|
517
|
+
(emits "try{")
|
|
518
|
+
(let [{:keys [statements ret]} try]
|
|
519
|
+
(emit-block subcontext statements ret))
|
|
520
|
+
(emits "}")
|
|
521
|
+
(when name
|
|
522
|
+
(emits "catch (" (munge name) "){")
|
|
523
|
+
(when catch
|
|
524
|
+
(let [{:keys [statements ret]} catch]
|
|
525
|
+
(emit-block subcontext statements ret)))
|
|
526
|
+
(emits "}"))
|
|
527
|
+
(when finally
|
|
528
|
+
(let [{:keys [statements ret]} finally]
|
|
529
|
+
(assert (not= :constant (:op ret)) "finally block cannot contain constant")
|
|
530
|
+
(emits "finally {")
|
|
531
|
+
(emit-block subcontext statements ret)
|
|
532
|
+
(emits "}")))
|
|
533
|
+
(when (= :expr context) (emits "})()")))
|
|
534
|
+
(let [{:keys [statements ret]} try]
|
|
535
|
+
(when (and statements (= :expr context)) (emits "(function (){"))
|
|
536
|
+
(emit-block subcontext statements ret)
|
|
537
|
+
(when (and statements (= :expr context)) (emits "})()"))))))
|
|
538
|
+
|
|
539
|
+
(defmethod emit :let
|
|
540
|
+
[{:keys [bindings statements ret env loop]}]
|
|
541
|
+
(let [context (:context env)]
|
|
542
|
+
(when (= :expr context) (emits "(function (){"))
|
|
543
|
+
(doseq [{:keys [init] :as binding} bindings]
|
|
544
|
+
(emitln "var " (munge binding) " = " init ";"))
|
|
545
|
+
(when loop (emitln "while(true){"))
|
|
546
|
+
(emit-block (if (= :expr context) :return context) statements ret)
|
|
547
|
+
(when loop
|
|
548
|
+
(emitln "break;")
|
|
549
|
+
(emitln "}"))
|
|
550
|
+
;(emits "}")
|
|
551
|
+
(when (= :expr context) (emits "})()"))))
|
|
552
|
+
|
|
553
|
+
(defmethod emit :recur
|
|
554
|
+
[{:keys [frame exprs env]}]
|
|
555
|
+
(let [temps (vec (take (count exprs) (repeatedly gensym)))
|
|
556
|
+
params (:params frame)]
|
|
557
|
+
(emitln "{")
|
|
558
|
+
(dotimes [i (count exprs)]
|
|
559
|
+
(emitln "var " (temps i) " = " (exprs i) ";"))
|
|
560
|
+
(dotimes [i (count exprs)]
|
|
561
|
+
(emitln (munge (params i)) " = " (temps i) ";"))
|
|
562
|
+
(emitln "continue;")
|
|
563
|
+
(emitln "}")))
|
|
564
|
+
|
|
565
|
+
(defmethod emit :letfn
|
|
566
|
+
[{:keys [bindings statements ret env]}]
|
|
567
|
+
(let [context (:context env)]
|
|
568
|
+
(when (= :expr context) (emits "(function (){"))
|
|
569
|
+
(doseq [{:keys [init] :as binding} bindings]
|
|
570
|
+
(emitln "var " (munge binding) " = " init ";"))
|
|
571
|
+
(emit-block (if (= :expr context) :return context) statements ret)
|
|
572
|
+
(when (= :expr context) (emits "})()"))))
|
|
573
|
+
|
|
574
|
+
(defn protocol-prefix [psym]
|
|
575
|
+
(symbol (str (-> (str psym) (.replace \. \$) (.replace \/ \$)) "$")))
|
|
576
|
+
|
|
577
|
+
(defmethod emit :invoke
|
|
578
|
+
[{:keys [f args env] :as expr}]
|
|
579
|
+
(let [info (:info f)
|
|
580
|
+
fn? (and ana/*cljs-static-fns*
|
|
581
|
+
(not (:dynamic info))
|
|
582
|
+
(:fn-var info))
|
|
583
|
+
protocol (:protocol info)
|
|
584
|
+
proto? (let [tag (infer-tag (first (:args expr)))]
|
|
585
|
+
(and protocol tag
|
|
586
|
+
(or ana/*cljs-static-fns*
|
|
587
|
+
(:protocol-inline env))
|
|
588
|
+
(or (= protocol tag)
|
|
589
|
+
(when-let [ps (:protocols (ana/resolve-existing-var (dissoc env :locals) tag))]
|
|
590
|
+
(ps protocol)))))
|
|
591
|
+
opt-not? (and (= (:name info) 'cljs.core/not)
|
|
592
|
+
(= (infer-tag (first (:args expr))) 'boolean))
|
|
593
|
+
ns (:ns info)
|
|
594
|
+
js? (= ns 'js)
|
|
595
|
+
goog? (when ns
|
|
596
|
+
(or (= ns 'goog)
|
|
597
|
+
(when-let [ns-str (str ns)]
|
|
598
|
+
(= (get (string/split ns-str #"\.") 0 nil) "goog"))))
|
|
599
|
+
keyword? (and (= (-> f :op) :constant)
|
|
600
|
+
(keyword? (-> f :form)))
|
|
601
|
+
[f variadic-invoke]
|
|
602
|
+
(if fn?
|
|
603
|
+
(let [arity (count args)
|
|
604
|
+
variadic? (:variadic info)
|
|
605
|
+
mps (:method-params info)
|
|
606
|
+
mfa (:max-fixed-arity info)]
|
|
607
|
+
(cond
|
|
608
|
+
;; if only one method, no renaming needed
|
|
609
|
+
(and (not variadic?)
|
|
610
|
+
(= (count mps) 1))
|
|
611
|
+
[f nil]
|
|
612
|
+
|
|
613
|
+
;; direct dispatch to variadic case
|
|
614
|
+
(and variadic? (> arity mfa))
|
|
615
|
+
[(update-in f [:info :name]
|
|
616
|
+
(fn [name] (symbol (str (munge name) ".cljs$lang$arity$variadic"))))
|
|
617
|
+
{:max-fixed-arity mfa}]
|
|
618
|
+
|
|
619
|
+
;; direct dispatch to specific arity case
|
|
620
|
+
:else
|
|
621
|
+
(let [arities (map count mps)]
|
|
622
|
+
(if (some #{arity} arities)
|
|
623
|
+
[(update-in f [:info :name]
|
|
624
|
+
(fn [name] (symbol (str (munge name) ".cljs$lang$arity$" arity)))) nil]
|
|
625
|
+
[f nil]))))
|
|
626
|
+
[f nil])]
|
|
627
|
+
(emit-wrap env
|
|
628
|
+
(cond
|
|
629
|
+
opt-not?
|
|
630
|
+
(emits "!(" (first args) ")")
|
|
631
|
+
|
|
632
|
+
proto?
|
|
633
|
+
(let [pimpl (str (munge (protocol-prefix protocol))
|
|
634
|
+
(munge (name (:name info))) "$arity$" (count args))]
|
|
635
|
+
(emits (first args) "." pimpl "(" (comma-sep args) ")"))
|
|
636
|
+
|
|
637
|
+
keyword?
|
|
638
|
+
(emits "(new cljs.core.Keyword(" f ")).call(" (comma-sep (cons "null" args)) ")")
|
|
639
|
+
|
|
640
|
+
variadic-invoke
|
|
641
|
+
(let [mfa (:max-fixed-arity variadic-invoke)]
|
|
642
|
+
(emits f "(" (comma-sep (take mfa args))
|
|
643
|
+
(when-not (zero? mfa) ",")
|
|
644
|
+
"cljs.core.array_seq([" (comma-sep (drop mfa args)) "], 0))"))
|
|
645
|
+
|
|
646
|
+
(or fn? js? goog?)
|
|
647
|
+
(emits f "(" (comma-sep args) ")")
|
|
648
|
+
|
|
649
|
+
:else
|
|
650
|
+
(if (and ana/*cljs-static-fns* (= (:op f) :var))
|
|
651
|
+
(let [fprop (str ".cljs$lang$arity$" (count args))]
|
|
652
|
+
(emits "(" f fprop " ? " f fprop "(" (comma-sep args) ") : " f ".call(" (comma-sep (cons "null" args)) "))"))
|
|
653
|
+
(emits f ".call(" (comma-sep (cons "null" args)) ")"))))))
|
|
654
|
+
|
|
655
|
+
(defmethod emit :new
|
|
656
|
+
[{:keys [ctor args env]}]
|
|
657
|
+
(emit-wrap env
|
|
658
|
+
(emits "(new " ctor "("
|
|
659
|
+
(comma-sep args)
|
|
660
|
+
"))")))
|
|
661
|
+
|
|
662
|
+
(defmethod emit :set!
|
|
663
|
+
[{:keys [target val env]}]
|
|
664
|
+
(emit-wrap env (emits target " = " val)))
|
|
665
|
+
|
|
666
|
+
(defmethod emit :ns
|
|
667
|
+
[{:keys [name requires uses requires-macros env]}]
|
|
668
|
+
(swap! ns-first-segments conj (first (string/split (str name) #"\.")))
|
|
669
|
+
(emitln "goog.provide('" (munge name) "');")
|
|
670
|
+
(when-not (= name 'cljs.core)
|
|
671
|
+
(emitln "goog.require('cljs.core');"))
|
|
672
|
+
(doseq [lib (into (vals requires) (distinct (vals uses)))]
|
|
673
|
+
(emitln "goog.require('" (munge lib) "');")))
|
|
674
|
+
|
|
675
|
+
(defmethod emit :deftype*
|
|
676
|
+
[{:keys [t fields pmasks]}]
|
|
677
|
+
(let [fields (map munge fields)]
|
|
678
|
+
(when-not (or (nil? *emitted-provides*) (contains? @*emitted-provides* t))
|
|
679
|
+
(swap! *emitted-provides* conj t)
|
|
680
|
+
(emitln "")
|
|
681
|
+
(emitln "goog.provide('" (munge t) "');"))
|
|
682
|
+
(emitln "")
|
|
683
|
+
(emitln "/**")
|
|
684
|
+
(emitln "* @constructor")
|
|
685
|
+
(emitln "*/")
|
|
686
|
+
(emitln (munge t) " = (function (" (comma-sep fields) "){")
|
|
687
|
+
(doseq [fld fields]
|
|
688
|
+
(emitln "this." fld " = " fld ";"))
|
|
689
|
+
(doseq [[pno pmask] pmasks]
|
|
690
|
+
(emitln "this.cljs$lang$protocol_mask$partition" pno "$ = " pmask ";"))
|
|
691
|
+
(emitln "})")))
|
|
692
|
+
|
|
693
|
+
(defmethod emit :defrecord*
|
|
694
|
+
[{:keys [t fields pmasks]}]
|
|
695
|
+
(let [fields (concat (map munge fields) '[__meta __extmap])]
|
|
696
|
+
(when-not (or (nil? *emitted-provides*) (contains? @*emitted-provides* t))
|
|
697
|
+
(swap! *emitted-provides* conj t)
|
|
698
|
+
(emitln "")
|
|
699
|
+
(emitln "goog.provide('" (munge t) "');"))
|
|
700
|
+
(emitln "")
|
|
701
|
+
(emitln "/**")
|
|
702
|
+
(emitln "* @constructor")
|
|
703
|
+
(doseq [fld fields]
|
|
704
|
+
(emitln "* @param {*} " fld))
|
|
705
|
+
(emitln "* @param {*=} __meta ")
|
|
706
|
+
(emitln "* @param {*=} __extmap")
|
|
707
|
+
(emitln "*/")
|
|
708
|
+
(emitln (munge t) " = (function (" (comma-sep fields) "){")
|
|
709
|
+
(doseq [fld fields]
|
|
710
|
+
(emitln "this." fld " = " fld ";"))
|
|
711
|
+
(doseq [[pno pmask] pmasks]
|
|
712
|
+
(emitln "this.cljs$lang$protocol_mask$partition" pno "$ = " pmask ";"))
|
|
713
|
+
(emitln "if(arguments.length>" (- (count fields) 2) "){")
|
|
714
|
+
(emitln "this.__meta = __meta;")
|
|
715
|
+
(emitln "this.__extmap = __extmap;")
|
|
716
|
+
(emitln "} else {")
|
|
717
|
+
(emits "this.__meta=")
|
|
718
|
+
(emit-constant nil)
|
|
719
|
+
(emitln ";")
|
|
720
|
+
(emits "this.__extmap=")
|
|
721
|
+
(emit-constant nil)
|
|
722
|
+
(emitln ";")
|
|
723
|
+
(emitln "}")
|
|
724
|
+
(emitln "})")))
|
|
725
|
+
|
|
726
|
+
(defmethod emit :dot
|
|
727
|
+
[{:keys [target field method args env]}]
|
|
728
|
+
(emit-wrap env
|
|
729
|
+
(if field
|
|
730
|
+
(emits target "." (munge field #{}))
|
|
731
|
+
(emits target "." (munge method #{}) "("
|
|
732
|
+
(comma-sep args)
|
|
733
|
+
")"))))
|
|
734
|
+
|
|
735
|
+
(defmethod emit :js
|
|
736
|
+
[{:keys [env code segs args]}]
|
|
737
|
+
(emit-wrap env
|
|
738
|
+
(if code
|
|
739
|
+
(emits code)
|
|
740
|
+
(emits (interleave (concat segs (repeat nil))
|
|
741
|
+
(concat args [nil]))))))
|
|
742
|
+
|
|
743
|
+
(defn forms-seq
|
|
744
|
+
"Seq of forms in a Clojure or ClojureScript file."
|
|
745
|
+
([f]
|
|
746
|
+
(forms-seq f (clojure.lang.LineNumberingPushbackReader. (io/reader f))))
|
|
747
|
+
([f ^java.io.PushbackReader rdr]
|
|
748
|
+
(if-let [form (binding [*ns* ana/*reader-ns*] (read rdr nil nil))]
|
|
749
|
+
(lazy-seq (cons form (forms-seq f rdr)))
|
|
750
|
+
(.close rdr))))
|
|
751
|
+
|
|
752
|
+
(defn rename-to-js
|
|
753
|
+
"Change the file extension from .cljs to .js. Takes a File or a
|
|
754
|
+
String. Always returns a String."
|
|
755
|
+
[file-str]
|
|
756
|
+
(clojure.string/replace file-str #"\.cljs$" ".js"))
|
|
757
|
+
|
|
758
|
+
(defn mkdirs
|
|
759
|
+
"Create all parent directories for the passed file."
|
|
760
|
+
[^java.io.File f]
|
|
761
|
+
(.mkdirs (.getParentFile (.getCanonicalFile f))))
|
|
762
|
+
|
|
763
|
+
(defmacro with-core-cljs
|
|
764
|
+
"Ensure that core.cljs has been loaded."
|
|
765
|
+
[& body]
|
|
766
|
+
`(do (when-not (:defs (get @ana/namespaces 'cljs.core))
|
|
767
|
+
(ana/analyze-file "cljs/core.cljs"))
|
|
768
|
+
~@body))
|
|
769
|
+
|
|
770
|
+
(defn compile-file* [src dest]
|
|
771
|
+
(with-core-cljs
|
|
772
|
+
(with-open [out ^java.io.Writer (io/make-writer dest {})]
|
|
773
|
+
(binding [*out* out
|
|
774
|
+
ana/*cljs-ns* 'cljs.user
|
|
775
|
+
ana/*cljs-file* (.getPath ^java.io.File src)
|
|
776
|
+
*data-readers* tags/*cljs-data-readers*
|
|
777
|
+
*position* (atom [0 0])
|
|
778
|
+
*emitted-provides* (atom #{})]
|
|
779
|
+
(loop [forms (forms-seq src)
|
|
780
|
+
ns-name nil
|
|
781
|
+
deps nil]
|
|
782
|
+
(if (seq forms)
|
|
783
|
+
(let [env (ana/empty-env)
|
|
784
|
+
ast (ana/analyze env (first forms))]
|
|
785
|
+
(do (emit ast)
|
|
786
|
+
(if (= (:op ast) :ns)
|
|
787
|
+
(recur (rest forms) (:name ast) (merge (:uses ast) (:requires ast)))
|
|
788
|
+
(recur (rest forms) ns-name deps))))
|
|
789
|
+
{:ns (or ns-name 'cljs.user)
|
|
790
|
+
:provides [ns-name]
|
|
791
|
+
:requires (if (= ns-name 'cljs.core) (set (vals deps)) (conj (set (vals deps)) 'cljs.core))
|
|
792
|
+
:file dest}))))))
|
|
793
|
+
|
|
794
|
+
(defn requires-compilation?
|
|
795
|
+
"Return true if the src file requires compilation."
|
|
796
|
+
[^java.io.File src ^java.io.File dest]
|
|
797
|
+
(or (not (.exists dest))
|
|
798
|
+
(> (.lastModified src) (.lastModified dest))))
|
|
799
|
+
|
|
800
|
+
(defn compile-file
|
|
801
|
+
"Compiles src to a file of the same name, but with a .js extension,
|
|
802
|
+
in the src file's directory.
|
|
803
|
+
|
|
804
|
+
With dest argument, write file to provided location. If the dest
|
|
805
|
+
argument is a file outside the source tree, missing parent
|
|
806
|
+
directories will be created. The src file will only be compiled if
|
|
807
|
+
the dest file has an older modification time.
|
|
808
|
+
|
|
809
|
+
Both src and dest may be either a String or a File.
|
|
810
|
+
|
|
811
|
+
Returns a map containing {:ns .. :provides .. :requires .. :file ..}.
|
|
812
|
+
If the file was not compiled returns only {:file ...}"
|
|
813
|
+
([src]
|
|
814
|
+
(let [dest (rename-to-js src)]
|
|
815
|
+
(compile-file src dest)))
|
|
816
|
+
([src dest]
|
|
817
|
+
(let [src-file (io/file src)
|
|
818
|
+
dest-file (io/file dest)]
|
|
819
|
+
(if (.exists src-file)
|
|
820
|
+
(if (requires-compilation? src-file dest-file)
|
|
821
|
+
(do (mkdirs dest-file)
|
|
822
|
+
(compile-file* src-file dest-file))
|
|
823
|
+
{:file dest-file})
|
|
824
|
+
(throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))
|
|
825
|
+
|
|
826
|
+
(comment
|
|
827
|
+
;; flex compile-file
|
|
828
|
+
(do
|
|
829
|
+
(compile-file "/tmp/hello.cljs" "/tmp/something.js")
|
|
830
|
+
(slurp "/tmp/hello.js")
|
|
831
|
+
|
|
832
|
+
(compile-file "/tmp/somescript.cljs")
|
|
833
|
+
(slurp "/tmp/somescript.js")))
|
|
834
|
+
|
|
835
|
+
(defn path-seq
|
|
836
|
+
[file-str]
|
|
837
|
+
(->> java.io.File/separator
|
|
838
|
+
java.util.regex.Pattern/quote
|
|
839
|
+
re-pattern
|
|
840
|
+
(string/split file-str)))
|
|
841
|
+
|
|
842
|
+
(defn to-path
|
|
843
|
+
([parts]
|
|
844
|
+
(to-path parts java.io.File/separator))
|
|
845
|
+
([parts sep]
|
|
846
|
+
(apply str (interpose sep parts))))
|
|
847
|
+
|
|
848
|
+
(defn to-target-file
|
|
849
|
+
"Given the source root directory, the output target directory and
|
|
850
|
+
file under the source root, produce the target file."
|
|
851
|
+
[^java.io.File dir ^String target ^java.io.File file]
|
|
852
|
+
(let [dir-path (path-seq (.getAbsolutePath dir))
|
|
853
|
+
file-path (path-seq (.getAbsolutePath file))
|
|
854
|
+
relative-path (drop (count dir-path) file-path)
|
|
855
|
+
parents (butlast relative-path)
|
|
856
|
+
parent-file (java.io.File. ^String (to-path (cons target parents)))]
|
|
857
|
+
(java.io.File. parent-file ^String (rename-to-js (last relative-path)))))
|
|
858
|
+
|
|
859
|
+
(defn cljs-files-in
|
|
860
|
+
"Return a sequence of all .cljs files in the given directory."
|
|
861
|
+
[dir]
|
|
862
|
+
(filter #(let [name (.getName ^java.io.File %)]
|
|
863
|
+
(and (.endsWith name ".cljs")
|
|
864
|
+
(not= \. (first name))
|
|
865
|
+
(not (contains? cljs-reserved-file-names name))))
|
|
866
|
+
(file-seq dir)))
|
|
867
|
+
|
|
868
|
+
(defn compile-root
|
|
869
|
+
"Looks recursively in src-dir for .cljs files and compiles them to
|
|
870
|
+
.js files. If target-dir is provided, output will go into this
|
|
871
|
+
directory mirroring the source directory structure. Returns a list
|
|
872
|
+
of maps containing information about each file which was compiled
|
|
873
|
+
in dependency order."
|
|
874
|
+
([src-dir]
|
|
875
|
+
(compile-root src-dir "out"))
|
|
876
|
+
([src-dir target-dir]
|
|
877
|
+
(let [src-dir-file (io/file src-dir)]
|
|
878
|
+
(loop [cljs-files (cljs-files-in src-dir-file)
|
|
879
|
+
output-files []]
|
|
880
|
+
(if (seq cljs-files)
|
|
881
|
+
(let [cljs-file (first cljs-files)
|
|
882
|
+
output-file ^java.io.File (to-target-file src-dir-file target-dir cljs-file)
|
|
883
|
+
ns-info (compile-file cljs-file output-file)]
|
|
884
|
+
(recur (rest cljs-files) (conj output-files (assoc ns-info :file-name (.getPath output-file)))))
|
|
885
|
+
output-files)))))
|
|
886
|
+
|
|
887
|
+
(comment
|
|
888
|
+
;; compile-root
|
|
889
|
+
;; If you have a standard project layout with all file in src
|
|
890
|
+
(compile-root "src")
|
|
891
|
+
;; will produce a mirrored directory structure under "out" but all
|
|
892
|
+
;; files will be compiled to js.
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
(comment
|
|
896
|
+
|
|
897
|
+
;;the new way - use the REPL!!
|
|
898
|
+
(require '[cljs.compiler :as comp])
|
|
899
|
+
(def repl-env (comp/repl-env))
|
|
900
|
+
(comp/repl repl-env)
|
|
901
|
+
;having problems?, try verbose mode
|
|
902
|
+
(comp/repl repl-env :verbose true)
|
|
903
|
+
;don't forget to check for uses of undeclared vars
|
|
904
|
+
(comp/repl repl-env :warn-on-undeclared true)
|
|
905
|
+
|
|
906
|
+
(test-stuff)
|
|
907
|
+
(+ 1 2 3)
|
|
908
|
+
([ 1 2 3 4] 2)
|
|
909
|
+
({:a 1 :b 2} :a)
|
|
910
|
+
({1 1 2 2} 1)
|
|
911
|
+
(#{1 2 3} 2)
|
|
912
|
+
(:b {:a 1 :b 2})
|
|
913
|
+
('b '{:a 1 b 2})
|
|
914
|
+
|
|
915
|
+
(extend-type number ISeq (-seq [x] x))
|
|
916
|
+
(seq 42)
|
|
917
|
+
;(aset cljs.core.ISeq "number" true)
|
|
918
|
+
;(aget cljs.core.ISeq "number")
|
|
919
|
+
(satisfies? ISeq 42)
|
|
920
|
+
(extend-type nil ISeq (-seq [x] x))
|
|
921
|
+
(satisfies? ISeq nil)
|
|
922
|
+
(seq nil)
|
|
923
|
+
|
|
924
|
+
(extend-type default ISeq (-seq [x] x))
|
|
925
|
+
(satisfies? ISeq true)
|
|
926
|
+
(seq true)
|
|
927
|
+
|
|
928
|
+
(test-stuff)
|
|
929
|
+
|
|
930
|
+
(array-seq [])
|
|
931
|
+
(defn f [& etc] etc)
|
|
932
|
+
(f)
|
|
933
|
+
|
|
934
|
+
(in-ns 'cljs.core)
|
|
935
|
+
;;hack on core
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
(deftype Foo [a] IMeta (-meta [_] (fn [] a)))
|
|
939
|
+
((-meta (Foo. 42)))
|
|
940
|
+
|
|
941
|
+
;;OLD way, don't you want to use the REPL?
|
|
942
|
+
(in-ns 'cljs.compiler)
|
|
943
|
+
(import '[javax.script ScriptEngineManager])
|
|
944
|
+
(def jse (-> (ScriptEngineManager.) (.getEngineByName "JavaScript")))
|
|
945
|
+
(.eval jse cljs.compiler/bootjs)
|
|
946
|
+
(def envx {:ns (@namespaces 'cljs.user) :context :expr :locals '{ethel {:name ethel__123 :init nil}}})
|
|
947
|
+
(analyze envx nil)
|
|
948
|
+
(analyze envx 42)
|
|
949
|
+
(analyze envx "foo")
|
|
950
|
+
(analyze envx 'fred)
|
|
951
|
+
(analyze envx 'fred.x)
|
|
952
|
+
(analyze envx 'ethel)
|
|
953
|
+
(analyze envx 'ethel.x)
|
|
954
|
+
(analyze envx 'my.ns/fred)
|
|
955
|
+
(analyze envx 'your.ns.fred)
|
|
956
|
+
(analyze envx '(if test then else))
|
|
957
|
+
(analyze envx '(if test then))
|
|
958
|
+
(analyze envx '(and fred ethel))
|
|
959
|
+
(analyze (assoc envx :context :statement) '(def test "fortytwo" 42))
|
|
960
|
+
(analyze (assoc envx :context :expr) '(fn* ^{::fields [a b c]} [x y] a y x))
|
|
961
|
+
(analyze (assoc envx :context :statement) '(let* [a 1 b 2] a))
|
|
962
|
+
(analyze (assoc envx :context :statement) '(defprotocol P (bar [a]) (baz [b c])))
|
|
963
|
+
(analyze (assoc envx :context :statement) '(. x y))
|
|
964
|
+
(analyze envx '(fn foo [x] (let [x 42] (js* "~{x}['foobar']"))))
|
|
965
|
+
|
|
966
|
+
(analyze envx '(ns fred (:require [your.ns :as yn]) (:require-macros [clojure.core :as core])))
|
|
967
|
+
(defmacro js [form]
|
|
968
|
+
`(emit (ana/analyze {:ns (@ana/namespaces 'cljs.user) :context :statement :locals {}} '~form)))
|
|
969
|
+
|
|
970
|
+
(defn jscapture [form]
|
|
971
|
+
"just grabs the js, doesn't print it"
|
|
972
|
+
(with-out-str
|
|
973
|
+
(emit (analyze {:ns (@namespaces 'cljs.user) :context :expr :locals {}} form))))
|
|
974
|
+
|
|
975
|
+
(defn jseval [form]
|
|
976
|
+
(let [js (jscapture form)]
|
|
977
|
+
;;(prn js)
|
|
978
|
+
(.eval jse (str "print(" js ")"))))
|
|
979
|
+
|
|
980
|
+
;; from closure.clj
|
|
981
|
+
(optimize (jscapture '(defn foo [x y] (if true 46 (recur 1 x)))))
|
|
982
|
+
|
|
983
|
+
(js (if a b c))
|
|
984
|
+
(js (def x 42))
|
|
985
|
+
(js (defn foo [a b] a))
|
|
986
|
+
(js (do 1 2 3))
|
|
987
|
+
(js (let [a 1 b 2 a b] a))
|
|
988
|
+
|
|
989
|
+
(js (ns fred (:require [your.ns :as yn]) (:require-macros [cljs.core :as core])))
|
|
990
|
+
|
|
991
|
+
(js (def foo? (fn* ^{::fields [a? b c]} [x y] (if true a? (recur 1 x)))))
|
|
992
|
+
(js (def foo (fn* ^{::fields [a b c]} [x y] (if true a (recur 1 x)))))
|
|
993
|
+
(js (defn foo [x y] (if true x y)))
|
|
994
|
+
(jseval '(defn foo [x y] (if true x y)))
|
|
995
|
+
(js (defn foo [x y] (if true 46 (recur 1 x))))
|
|
996
|
+
(jseval '(defn foo [x y] (if true 46 (recur 1 x))))
|
|
997
|
+
(jseval '(foo 1 2))
|
|
998
|
+
(js (and fred ethel))
|
|
999
|
+
(jseval '(ns fred (:require [your.ns :as yn]) (:require-macros [cljs.core :as core])))
|
|
1000
|
+
(js (def x 42))
|
|
1001
|
+
(jseval '(def x 42))
|
|
1002
|
+
(jseval 'x)
|
|
1003
|
+
(jseval '(if 42 1 2))
|
|
1004
|
+
(jseval '(or 1 2))
|
|
1005
|
+
(jseval '(fn* [x y] (if true 46 (recur 1 x))))
|
|
1006
|
+
(.eval jse "print(test)")
|
|
1007
|
+
(.eval jse "print(cljs.user.Foo)")
|
|
1008
|
+
(.eval jse "print(cljs.user.Foo = function (){\n}\n)")
|
|
1009
|
+
(js (def fred 42))
|
|
1010
|
+
(js (deftype* Foo [a b-foo c]))
|
|
1011
|
+
(jseval '(deftype* Foo [a b-foo c]))
|
|
1012
|
+
(jseval '(. (new Foo 1 2 3) b-foo))
|
|
1013
|
+
(js (. (new Foo 1 2 3) b))
|
|
1014
|
+
(.eval jse "print(new cljs.user.Foo(1, 42, 3).b)")
|
|
1015
|
+
(.eval jse "(function (x, ys){return Array.prototype.slice.call(arguments, 1);})(1,2)[0]")
|
|
1016
|
+
|
|
1017
|
+
(macroexpand-1 '(cljs.core/deftype Foo [a b c] Fred (fred [x] a) (fred [x y] b) (ethel [x] c) Ethel (foo [] d)))
|
|
1018
|
+
(-> (macroexpand-1 '(cljs.core/deftype Foo [a b c] Fred (fred [x] a) (fred [x y] b) (ethel [x] c) Ethel (foo [] d)))
|
|
1019
|
+
last last last first meta)
|
|
1020
|
+
|
|
1021
|
+
(macroexpand-1 '(cljs.core/extend-type Foo Fred (fred ([x] a) ([x y] b)) (ethel ([x] c)) Ethel (foo ([] d))))
|
|
1022
|
+
(js (new foo.Bar 65))
|
|
1023
|
+
(js (defprotocol P (bar [a]) (baz [b c])))
|
|
1024
|
+
(js (. x y))
|
|
1025
|
+
(js (. "fred" (y)))
|
|
1026
|
+
(js (. x y 42 43))
|
|
1027
|
+
(js (.. a b c d))
|
|
1028
|
+
(js (. x (y 42 43)))
|
|
1029
|
+
(js (fn [x] x))
|
|
1030
|
+
(js (fn ([t] t) ([x y] y) ([ a b & zs] b)))
|
|
1031
|
+
|
|
1032
|
+
(js (. (fn foo ([t] t) ([x y] y) ([a b & zs] b)) call nil 1 2))
|
|
1033
|
+
(js (fn foo
|
|
1034
|
+
([t] t)
|
|
1035
|
+
([x y] y)
|
|
1036
|
+
([ a b & zs] b)))
|
|
1037
|
+
|
|
1038
|
+
(js ((fn foo
|
|
1039
|
+
([t] (foo t nil))
|
|
1040
|
+
([x y] y)
|
|
1041
|
+
([ a b & zs] b)) 1 2 3))
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
(jseval '((fn foo ([t] t) ([x y] y) ([ a b & zs] zs)) 12 13 14 15))
|
|
1045
|
+
|
|
1046
|
+
(js (defn foo [this] this))
|
|
1047
|
+
|
|
1048
|
+
(js (defn foo [a b c & ys] ys))
|
|
1049
|
+
(js ((fn [x & ys] ys) 1 2 3 4))
|
|
1050
|
+
(jseval '((fn [x & ys] ys) 1 2 3 4))
|
|
1051
|
+
(js (cljs.core/deftype Foo [a b c] Fred (fred [x] a) (fred [x y] a) (ethel [x] c) Ethel (foo [] d)))
|
|
1052
|
+
(jseval '(cljs.core/deftype Foo [a b c] Fred (fred [x] a) (fred [x y] a) (ethel [x] c) Ethel (foo [] d)))
|
|
1053
|
+
|
|
1054
|
+
(js (do
|
|
1055
|
+
(defprotocol Proto (foo [this]))
|
|
1056
|
+
(deftype Type [a] Proto (foo [this] a))
|
|
1057
|
+
(foo (new Type 42))))
|
|
1058
|
+
|
|
1059
|
+
(jseval '(do
|
|
1060
|
+
(defprotocol P-roto (foo? [this]))
|
|
1061
|
+
(deftype T-ype [a] P-roto (foo? [this] a))
|
|
1062
|
+
(foo? (new T-ype 42))))
|
|
1063
|
+
|
|
1064
|
+
(js (def x (fn foo [x] (let [x 42] (js* "~{x}['foobar']")))))
|
|
1065
|
+
(js (let [a 1 b 2 a b] a))
|
|
1066
|
+
|
|
1067
|
+
(doseq [e '[nil true false 42 "fred" fred ethel my.ns/fred your.ns.fred
|
|
1068
|
+
(if test then "fooelse")
|
|
1069
|
+
(def x 45)
|
|
1070
|
+
(do x y y)
|
|
1071
|
+
(fn* [x y] x y x)
|
|
1072
|
+
(fn* [x y] (if true 46 (recur 1 x)))
|
|
1073
|
+
(let* [a 1 b 2 a a] a b)
|
|
1074
|
+
(do "do1")
|
|
1075
|
+
(loop* [x 1 y 2] (if true 42 (do (recur 43 44))))
|
|
1076
|
+
(my.foo 1 2 3)
|
|
1077
|
+
(let* [a 1 b 2 c 3] (set! y.s.d b) (new fred.Ethel a b c))
|
|
1078
|
+
(let [x (do 1 2 3)] x)
|
|
1079
|
+
]]
|
|
1080
|
+
(->> e (analyze envx) emit)
|
|
1081
|
+
(newline)))
|