Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Send inline source map information for form evaluation
When evaluating forms from a browser repl it is nice to see and get errors
referencing source information. To achieve that:

1. Send a sourceMappingURL with a data url using a base64 encoded
source map
2. Include sourcesContent in the source map based on the form being evaluated
3. Use sourceURL to name the eval block as the generated file
referenced in the source map.

This patch uses the :source metadata from tools.reader as the value to send
back to the browser when possible.  In the case of something without metadata
(string, number, etc) it will use (pr) to get the string representation.

Source maps are only generated and sent for a repl-env with a truthy
:source-map field, so the browser-repl has been changed to merge that value.
Make work
  • Loading branch information
xeqi authored and swannodette committed Nov 29, 2013
1 parent cd18069 commit f02775e
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/clj/cljs/analyzer.clj
Expand Up @@ -1385,7 +1385,7 @@

(defn analyze-wrap-meta [expr]
(let [form (:form expr)
m (dissoc (meta form) :line :column :end-column :end-line)]
m (dissoc (meta form) :line :column :end-column :end-line :source)]
(if (seq m)
(let [env (:env expr) ; take on expr's context ourselves
expr (assoc-in expr [:env :context] :expr) ; change expr to :expr
Expand Down
74 changes: 54 additions & 20 deletions src/clj/cljs/repl.clj
Expand Up @@ -8,14 +8,18 @@

(ns cljs.repl
(:refer-clojure :exclude [load-file])
(:import java.io.File)
(:import java.io.File
javax.xml.bind.DatatypeConverter)
(:require [clojure.string :as string]
[clojure.java.io :as io]
[cljs.compiler :as comp]
[cljs.analyzer :as ana]
[cljs.env :as env]
[cljs.tagged-literals :as tags]
[cljs.closure :as cljsc]))
[cljs.closure :as cljsc]
[cljs.source-map :as sm]
[clojure.tools.reader :as reader]
[clojure.tools.reader.reader-types :as readers]))

(def ^:dynamic *cljs-verbose* false)

Expand Down Expand Up @@ -76,7 +80,30 @@
(try
(let [ast (ana/analyze env form)
js (comp/emit-str ast)
wrap-js (comp/emit-str (ana/no-warn (ana/analyze env (wrap form))))]
wrap-js
(if (:source-map repl-env)
(binding [comp/*source-map-data*
(atom {:source-map (sorted-map)
:gen-col 0
:gen-line 0})]
(let [js (comp/emit-str (ana/no-warn (ana/analyze env (wrap form))))
t (System/currentTimeMillis)]
(str js
"\n//@ sourceURL=repl-" t ".js"
"\n//@ sourceMappingURL=data:application/json;base64,"
(DatatypeConverter/printBase64Binary
(.getBytes
(sm/encode
{(str "repl-" t ".cljs")
(:source-map @comp/*source-map-data*)}
{:lines (+ (:gen-line @comp/*source-map-data*) 3)
:file (str "repl-" t ".js")
:sources-content
[(or (:source (meta form))
;; handle strings / primitives without metadata
(with-out-str (pr form)))]})
"UTF-8")))))
(comp/emit-str (ana/no-warn (ana/analyze env (wrap form)))))]
(when (= (:op ast) :ns)
(load-dependencies repl-env (into (vals (:requires ast))
(distinct (vals (:uses ast))))))
Expand Down Expand Up @@ -172,27 +199,34 @@
is-special-fn? (set (keys special-fns))
read-error (Object.)]
(-setup repl-env)
(loop [forms (ana/forms-seq *in* "NO_SOURCE_FILE")]
(loop []
(print (str "ClojureScript:" ana/*cljs-ns* "> "))
(flush)
(let [form (try
(if (seq forms)
(first forms)
:cljs/quit)
(let [rdr (readers/source-logging-push-back-reader
(java.io.PushbackReader. (io/reader *in*))
1
"NO_SOURCE_FILE")
form (try
(binding [*ns* (create-ns ana/*cljs-ns*)
reader/*data-readers* tags/*cljs-data-readers*
reader/*alias-map*
(apply merge
((juxt :requires :require-macros)
(ana/get-namespace ana/*cljs-ns*)))]
(reader/read rdr nil read-error))
(catch Exception e
(println (.getMessage e))
read-error))]
(cond
(identical? form read-error) (recur (ana/forms-seq *in* "NO_SOURCE_FILE"))

(= form :cljs/quit) :quit

(and (seq? form) (is-special-fn? (first form)))
(do (apply (get special-fns (first form)) repl-env (rest form))
(newline)
(recur (rest forms)))

:else
(do (eval-and-print repl-env env form)
(recur (rest forms))))))
(identical? form read-error) (recur)
(= form :cljs/quit) :quit

(and (seq? form) (is-special-fn? (first form)))
(do (apply (get special-fns (first form)) repl-env (rest form))
(newline)
(recur))

:else
(do (eval-and-print repl-env env form)
(recur)))))
(-tear-down repl-env)))))
3 changes: 2 additions & 1 deletion src/clj/cljs/repl/browser.clj
Expand Up @@ -252,7 +252,8 @@
:static-dir ["." "out/"]
:preloaded-libs []
:src "src/"
:cljs.env/compiler compiler-env}
:cljs.env/compiler compiler-env
:source-map true}
opts)]
(cljs.env/with-compiler-env compiler-env
(reset! preloaded-libs (set (concat (always-preload) (map str (:preloaded-libs opts)))))
Expand Down
33 changes: 18 additions & 15 deletions src/clj/cljs/source_map.clj
Expand Up @@ -211,21 +211,24 @@
(doseq [[col infos] cols]
(encode-cols infos source-idx line col))))
(let [source-map-file-contents
{"version" 3
"file" (:file opts)
"sources" (into []
(let [paths (keys m)
f (if (or (:output-dir opts) (:source-map-path opts))
#(relativize-path % opts)
#(last (string/split % #"/")))]
(map f paths)))
"lineCount" (:lines opts)
"mappings" (->> (lines->segs @lines)
(map #(string/join "," %))
(string/join ";"))
"names" (into []
(map (set/map-invert @names->idx)
(range (count @names->idx))))}]
(cond-> {"version" 3
"file" (:file opts)
"sources" (into []
(let [paths (keys m)
f (if (or (:output-dir opts)
(:source-map-path opts))
#(relativize-path % opts)
#(last (string/split % #"/")))]
(map f paths)))
"lineCount" (:lines opts)
"mappings" (->> (lines->segs @lines)
(map #(string/join "," %))
(string/join ";"))
"names" (into []
(map (set/map-invert @names->idx)
(range (count @names->idx))))}
(:sources-content opts)
(assoc "sourcesContent" (:sources-content opts)))]
(with-out-str
(json/pprint
source-map-file-contents
Expand Down

0 comments on commit f02775e

Please sign in to comment.