               (concat (rest (map call->function mapped-frames)) [{}])))))))
 
 (defn mapped-stacktrace-str
-  "Given a vector representing the canonicalized JavaScript stacktrace
-   print the ClojureScript stacktrace. See mapped-stacktrace."
-  ([stacktrace sm]
-   (mapped-stacktrace-str stacktrace sm nil))
-  ([stacktrace sm opts]
+  "Given a vector representing the canonicalized JavaScript stacktrace and a map
+  of library names to decoded source maps, print the ClojureScript stacktrace .
+  See mapped-stacktrace."
+  ([stacktrace sms]
+   (mapped-stacktrace-str stacktrace sms nil))
+  ([stacktrace sms opts]
    (with-out-str
      (doseq [{:keys [function file line column]}
-             (mapped-stacktrace stacktrace sm opts)]
+             (mapped-stacktrace stacktrace sms opts)]
        (println "\t"
          (str (when function (str function " "))
               "(" file (when line (str ":" line))

                   unmunged-call-name
                   munged-fn-name))
               function call))]
     (let [mapped-frames (map (memoize #(mapped-frame % sm opts)) stacktrace)]
       ;; take each non-nil :call and optionally merge it into :function one-level
       ;; up to avoid replacing with local symbols, we only replace munged name if
       ;; we can munge call symbol back to it

;; -----------------------------------------------------------------------------
;; Stacktrace Mapping

(defn remove-ext [file]
  (-> file
    (string/replace #"\.js$" "")
    (string/replace #"\.cljs$" "")
    (string/replace #"\.cljc$" "")
    (string/replace #"\.clj$" "")))

(defn mapped-line-column-call
  "Given a cljs.source-map source map data structure map a generated line
   and column back to the original line, column, and function called."
  [sms file line column]
  (let [source-map (get sms (symbol (string/replace (remove-ext file) "/" ".")))]
    ;; source maps are 0 indexed for columns
    ;; multiple segments may exist at column
    ;; the last segment seems most accurate
    (letfn [(get-best-column [columns column]
             (last (or (get columns
                         (last (filter #(<= % (dec column))
                                 (sort (keys columns)))))
                     (second (first columns)))))
           (adjust [mapped]
             (vec (map #(%1 %2) [inc inc identity] mapped)))]
     (let [default [line column nil]]
       ;; source maps are 0 indexed for lines
       (if-let [columns (get source-map (dec line))]
         (adjust (map (get-best-column columns column) [:line :col :name]))
         default)))))

(defn mapped-frame
  "Given opts and a canonicalized JavaScript stacktrace frame, return the
  ClojureScript frame."
  [{:keys [function file line column]} sms opts]
  (let [no-source-file?      (if-not file true (starts-with? file "<"))
        [line' column' call] (if no-source-file?
                               [line column nil]
                               (mapped-line-column-call sms file line column))
        file'                (when-not no-source-file?
                               (if (ends-with? file ".js")
                                 (str (subs file 0 (- (count file) 3)) ".cljs")
                                 file))]
    {:function function
     :call     call
     :file     (if no-source-file?
