             [shadow.jvm-log :as log]
             [shadow.build.async :as async])
   (:import (java.util.concurrent ExecutorService)
-           (java.io File StringReader PushbackReader)))
+           (java.io File StringReader PushbackReader StringWriter)))
 
 (def SHADOW-TIMESTAMP
   ;; timestamp to ensure that new shadow-cljs release always invalidate caches

             (not (is-cache-blocked? state rc)))]

    (or (when (and cache? (not (contains? previously-compiled resource-id)))
          (load-cached-cljs-resource state rc))
        (let [source
              (data/get-source-code state rc)

              output
              (try
                (-> (do-compile-cljs-resource state rc source)
                    (compact-source-map))
                (catch Exception e
                  (let [{:keys [type ex-kind line] :as data}
                        (ex-data e)

                 {:read-cond :allow :features reader-features}))
        in (readers/indexing-push-back-reader (PushbackReader. (StringReader. cljs-source)) 1 resource-name)]

    (loop [{:keys [ns ns-info] :as compile-state} init]
      (let [ns
            (if-not (:macros-ns compile-state)
              ns
              (-> (str ns)
                  (str/replace #"\$macros" "")
                  (symbol)))

            form
            (binding [*ns*
                      (create-ns ns)

                      ana/*cljs-ns*
                      ns

                      ana/*cljs-file*
                      resource-name

                      reader/*data-readers*
                      tags/*cljs-data-readers*

                      reader/*alias-map*
                      (merge reader/*alias-map*
                        (:requires ns-info)
                        (:require-macros ns-info))]
              (reader/read opts in))]

        (if (identical? form eof-sentinel)
          ;; eof
          compile-state
          (recur (reduce-fn compile-state form)))))

    ))

(defmulti shadow-emit
  (fn [build-state ast]
