   ([form env opts]
      (prewalk (analyze form env opts) cleanup)))
 
-;; TODO: handle env/*compiler*, cache analyzed code
 (defn analyze-ns [ns env]
-  (let [res (ns-resource ns)]
-    (assert res (str "Can't find " ns " in classpath"))
-    (let [filename (source-path res)]
-      (binding [*ns* *ns*]
-        (with-open [rdr (io/reader res)]
-          (let [pbr (readers/indexing-push-back-reader
-                     (java.io.PushbackReader. rdr) 1 filename)
-                eof (Object.)
-                env (empty-env)]
-            (loop []
-              (let [form (binding [c.c/*ns* (create-ns *ns*)
-                                   reader/*data-readers* tags/*cljs-data-readers*
-                                   reader/*alias-map* (apply merge {}
-                                                             (-> env :namespaces deref (get *ns*)
-                                                                (select-keys #{:aliases :macro-aliases})
-                                                                vals))]
-                           (reader/read pbr nil eof))]
-                (when-not (identical? form eof)
-                  (analyze form (assoc env :ns *ns*))
-                  (recur))))))))))
+  (env/ensure (global-env)
+    (let [res (ns-resource ns)]
+      (assert res (str "Can't find " ns " in classpath"))
+      (let [filename (source-path res)
+            path (res-path res)]
+        (when-not (get-in *env* [::analyzed-cljs path])
+          (binding [*ns* *ns*]
+            (with-open [rdr (io/reader res)]
+              (let [pbr (readers/indexing-push-back-reader
+                         (java.io.PushbackReader. rdr) 1 filename)
+                    eof (Object.)
+                    env (empty-env)]
+                (loop []
+                  (let [form (binding [c.c/*ns* (create-ns *ns*)
+                                       reader/*data-readers* tags/*cljs-data-readers*
+                                       reader/*alias-map* (apply merge {}
+                                                                 (-> (env/deref-env) :namespaces (get *ns*)
+                                                                    (select-keys #{:aliases :macro-aliases})
+                                                                    vals))]
+                               (reader/read pbr nil eof))]
+                    (when-not (identical? form eof)
+                      (swap! *env* update-in [::analyzed-cljs path]
+                             (fnil conj [])
+                             (analyze form (assoc env :ns *ns*)))
+                      (recur))))))))))))

        :children [:args]}))))

(declare analyze-ns)
(defn ensure-loaded [ns {:keys [namespaces] :as env}]
  (or (@namespaces ns)
      (analyze-ns ns env)))

(defn populate-env
  [{:keys [import require require-macros refer-clojure]} ns-name {:keys [namespaces] :as env}]
  (let [imports (reduce-kv (fn [m prefix suffixes]
                             (merge m (into {} (mapv (fn [s] [s (symbol (str prefix "." s))]) suffixes)))) {} import)
        require-aliases (reduce (fn [m [ns {:keys [as]}]]


(def ^:dynamic *ns* 'cljs.user)

(defn global-env []
  (atom '{:namespaces {cljs.user {:mappings       {}
                                  :aliases        {}
                                  :macro-mappings {}
                                  :macro-aliases  {}
                                  :ns             cljs.user}
                       cljs.core {:mappings       {}
                                  :aliases        {}
                                  :macro-mappings {}
                                  :macro-aliases  {}
                                  :ns             cljs.core}}}))

(defn empty-env
  "Returns an empty env map"
  []
  {:context    :statement
   :locals     {}
   :ns         *ns*})

(defn desugar-host-expr [[op & expr :as form]]
  (if (symbol? op)
