diff --git a/deps.edn b/deps.edn
index 03fea05ea..82f1a4324 100644
--- a/deps.edn
+++ b/deps.edn
@@ -5,7 +5,7 @@
org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/core.specs.alpha {:mvn/version "0.1.24"}
org.clojure/data.json {:mvn/version "0.2.6"}
- org.clojure/google-closure-library {:mvn/version "0.0-20201211-3e6c510d"}
+ org.clojure/google-closure-library {:mvn/version "0.0-20211011-0726fdeb"}
org.clojure/spec.alpha {:mvn/version "0.1.143"}
org.clojure/tools.reader {:mvn/version "1.3.3"}
org.clojure/test.check {:mvn/version "0.10.0-alpha3"}}
diff --git a/pom.template.xml b/pom.template.xml
index 2161fff9d..77f35e8bd 100644
--- a/pom.template.xml
+++ b/pom.template.xml
@@ -35,7 +35,7 @@
org.clojure
google-closure-library
- 0.0-20201211-3e6c510d
+ 0.0-20210811-6da97fe1
org.clojure
diff --git a/project.clj b/project.clj
index 402b11d14..27d0ddd13 100644
--- a/project.clj
+++ b/project.clj
@@ -15,7 +15,7 @@
[org.clojure/tools.reader "1.3.3"]
[org.clojure/test.check "0.10.0-alpha3" :scope "test"]
[com.cognitect/transit-clj "0.8.309"]
- [org.clojure/google-closure-library "0.0-20201211-3e6c510d"]
+ [org.clojure/google-closure-library "0.0-20210811-6da97fe1"]
[com.google.javascript/closure-compiler-unshaded "v20210808"]]
:profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}
:uberjar {:aot :all :main cljs.main}
diff --git a/script/bootstrap b/script/bootstrap
index 8455dac4b..45e4838e0 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -8,7 +8,7 @@ CORE_SPECS_ALPHA_RELEASE="0.1.24"
CLOSURE_RELEASE="20210808"
DJSON_RELEASE="0.2.6"
TRANSIT_RELEASE="0.8.309"
-GCLOSURE_LIB_RELEASE="0.0-20201211-3e6c510d"
+GCLOSURE_LIB_RELEASE="0.0-20210811-6da97fe1"
TREADER_RELEASE="1.3.3"
TEST_CHECK_RELEASE="0.10.0-alpha3"
diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index d316c559f..aca00175d 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -2284,6 +2284,10 @@ reduces them without incurring seq initialization"
(not (identical? n js/Infinity))
(== (js/parseFloat n) (js/parseInt n 10))))
+(def
+ ^{:doc "INTERNAL: do not use"}
+ LongImpl goog.math.Long)
+
(defn int?
"Return true if x satisfies integer? or is an instance of goog.math.Integer
or goog.math.Long."
diff --git a/src/main/cljs/cljs/loader.cljs b/src/main/cljs/cljs/loader.cljs
index 33405cd47..473d0e5b8 100644
--- a/src/main/cljs/cljs/loader.cljs
+++ b/src/main/cljs/cljs/loader.cljs
@@ -9,8 +9,8 @@
(ns cljs.loader
(:require [goog.object :as gobj]
[goog.html.legacyconversions :as legacy])
- (:import [goog.module ModuleLoader]
- [goog.module ModuleManager]))
+ (:import [goog.module ModuleManager]
+ [goog.module ModuleLoader]))
(def module-infos MODULE_INFOS) ;; set by compiler
(def module-uris
diff --git a/src/main/cljs/clojure/browser/repl.cljs b/src/main/cljs/clojure/browser/repl.cljs
index 891f7a03b..2010f8c51 100644
--- a/src/main/cljs/clojure/browser/repl.cljs
+++ b/src/main/cljs/clojure/browser/repl.cljs
@@ -227,7 +227,10 @@
(let [ret (.require__ js/goog src)]
(when (= reload "reload-all")
(set! (.-cljsReloadAll_ js/goog) false))
- ret))))))
+ ;; handle requires from Closure Library goog.modules
+ (if (js/goog.isInModuleLoader_)
+ (js/goog.module.getInternal_ src)
+ ret)))))))
(defn connect
"Connects to a REPL server from an HTML document. After the
diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index c70eabc7e..12a6bab46 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -812,6 +812,12 @@
(or (contains? global-exports (symbol module))
(contains? global-exports (name module)))))
+(defn goog-module-dep?
+ [module]
+ (let [[module _] (lib&sublib module)
+ module-type (get-in @env/*compiler* [:js-dependency-index (str module) :module])]
+ (= :goog module-type)))
+
(defn confirm-var-exists
([env prefix suffix]
(let [warn (confirm-var-exist-warning env prefix suffix)]
@@ -1010,6 +1016,12 @@
(str "node$module$" (munge (string/replace (str name) #"[.\/]" #?(:clj "\\$"
:cljs "$$")))))
+(defn munge-goog-module-lib
+ ([name]
+ (str "goog$module$" (munge (string/replace (str name) #"[.\/]" #?(:clj "\\$" :cljs "$$")))))
+ ([ns name]
+ (str (munge ns) "." (munge-goog-module-lib name))))
+
(defn munge-global-export [name]
(str "global$module$" (munge (string/replace (str name) #"[.\/]" #?(:clj "\\$"
:cljs "$$")))))
@@ -1031,6 +1043,7 @@
(defn ns->module-type [ns]
(cond
+ (goog-module-dep? ns) :goog-module
(js-module-exists? ns) :js
(node-module-dep? ns) :node
(dep-has-global-exports? ns) :global))
@@ -1072,6 +1085,12 @@
:op :js-var
:foreign true}))
+(defmethod resolve* :goog-module
+ [env sym full-ns current-ns]
+ {:name (symbol (str current-ns) (str (munge-goog-module-lib full-ns) "." (name sym)))
+ :ns current-ns
+ :op :var})
+
(defmethod resolve* :global
[env sym full-ns current-ns]
(let [pre (extern-pre sym current-ns)]
@@ -1135,6 +1154,15 @@
:op :js-var
:ns current-ns})))
+(defn resolve-import
+ "goog.modules are deterministically assigned to a property of the namespace,
+ we cannot expect the reference will be globally available, so we resolve to
+ namespace local reference."
+ [env import]
+ (if (goog-module-dep? import)
+ (symbol (munge-goog-module-lib (-> env :ns :name) import))
+ import))
+
;; core.async calls `macroexpand-1` manually with an ill-formed
;; :locals map. Normally :locals maps symbols maps, but
;; core.async adds entries mapping symbols to symbols. We work
@@ -1207,7 +1235,13 @@
;; check if prefix is some existing def
(if-let [resolved (resolve-var env prefix nil false)]
(update resolved :name #(symbol (str % "." suffix)))
- (let [idx (.lastIndexOf s ".")
+ ;; glib imports (i.e. (:import [goog.module ModuleLoader])
+ ;; are always just dotted symbols after the recursion
+ (let [s (str
+ (cond->> s
+ (goog-module-dep? sym)
+ (resolve-import env)))
+ idx (.lastIndexOf (str s) ".")
pre (subs s 0 idx)
suf (subs s (inc idx))]
{:op :var
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index 7415678f7..cc5675a32 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -1294,6 +1294,9 @@
(let [{node-libs true libs-to-load false} (group-by ana/node-module-dep? libs)]
[node-libs libs-to-load])
[nil libs]))
+ [goog-modules libs-to-load] (let [{goog-modules true libs-to-load false}
+ (group-by ana/goog-module-dep? libs-to-load)]
+ [goog-modules libs-to-load])
global-exports-libs (filter ana/dep-has-global-exports? libs-to-load)]
(when (-> libs meta :reload-all)
(emitln "if(!COMPILED) " loaded-libs-temp " = " loaded-libs " || cljs.core.set([\"cljs.core\"]);")
@@ -1336,11 +1339,26 @@
:else
(when-not (= lib 'goog)
(emitln "goog.require('" (munge lib) "');"))))
+ ;; Node Libraries
(doseq [lib node-libs]
(let [[lib' sublib] (ana/lib&sublib lib)]
(emitln (munge ns-name) "."
(ana/munge-node-lib lib)
" = require('" lib' "')" (sublib-select sublib) ";")))
+ ;; Google Closure Library Modules (i.e. goog.module(...))
+ ;; these must be assigned to vars
+ (doseq [lib goog-modules]
+ (let [[lib' sublib] (ana/lib&sublib lib)]
+ (emitln "goog.require('" lib' "');")
+ ;; we emit goog.scope here to suppress a Closure error about
+ ;; goog.module.get when compiling - meant to discourage incorrect
+ ;; usage by hand written code - not applicable here
+ (emitln "goog.scope(function(){")
+ (emitln (munge ns-name) "."
+ (ana/munge-goog-module-lib lib)
+ " = goog.module.get('" lib' "')" (sublib-select sublib) ";")
+ (emitln "});")))
+ ;; Global Exports
(doseq [lib global-exports-libs]
(let [{:keys [global-exports]} (get js-dependency-index (name (-> lib ana/lib&sublib first)))]
(emit-global-export ns-name global-exports lib)))
diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index ab3bc3bf9..63ad476e9 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -1430,9 +1430,9 @@
(core/let [psym (resolve p)
pfn-prefix (subs (core/str psym) 0
(clojure.core/inc (.indexOf (core/str psym) "/")))]
- (cons `(goog.object/set ~psym ~type true)
+ (cons `(unchecked-set ~psym ~type true)
(map (core/fn [[f & meths :as form]]
- `(goog.object/set ~(symbol (core/str pfn-prefix f))
+ `(unchecked-set ~(symbol (core/str pfn-prefix f))
~type ~(with-meta `(fn ~@meths) (meta form))))
sigs))))
@@ -2672,8 +2672,8 @@
(js-obj* '())
`(let [~@(apply concat (clojure.set/map-invert expr->local))
~obj ~(js-obj* (filter-on-keys core/string? kvs))]
- ~@(map (core/fn [[k v]] `(goog.object/set ~obj ~k ~v)) sym-pairs)
- ~@(map (core/fn [[k v]] `(goog.object/set ~obj ~v ~(core/get kvs k))) expr->local)
+ ~@(map (core/fn [[k v]] `(unchecked-set ~obj ~k ~v)) sym-pairs)
+ ~@(map (core/fn [[k v]] `(unchecked-set ~obj ~v ~(core/get kvs k))) expr->local)
~obj))))
(core/defmacro alength [a]
@@ -2888,7 +2888,7 @@
(core/list 'js* "''+~{}" s))
(core/defmacro es6-iterable [ty]
- `(goog.object/set (.-prototype ~ty) cljs.core/ITER_SYMBOL
+ `(unchecked-set (.-prototype ~ty) cljs.core/ITER_SYMBOL
(fn []
(this-as this#
(cljs.core/es6-iterator this#)))))
diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj
index 37008ad49..b7b42fa36 100644
--- a/src/main/clojure/cljs/externs.clj
+++ b/src/main/clojure/cljs/externs.clj
@@ -16,7 +16,6 @@
[com.google.javascript.jscomp.parsing Config$JsDocParsing]
[com.google.javascript.rhino
Node Token JSTypeExpression JSDocInfo$Visibility]
- [java.nio.charset StandardCharsets]
[java.util.logging Level]))
(def ^:dynamic *ignore-var* false)
@@ -26,7 +25,10 @@
;; ------------------------------------------------------------------------------
;; Externs Parsing
-(defn annotate [props ty]
+(defn annotate
+ "Given a sequential list of properties [foo core baz] representing segments
+ of the namespace, annotate the last symbol with the type information."
+ [props ty]
(when (seq props)
(conj
(into [] (butlast props))
@@ -35,7 +37,8 @@
(defn get-tag [^JSTypeExpression texpr]
(when-let [root (.getRoot texpr)]
(if (.isString root)
- (symbol (.getString root))(if-let [child (.. root getFirstChild)]
+ (symbol (.getString root))
+ (if-let [child (.. root getFirstChild)]
(if (.isString child)
(symbol (.. child getString)))))))
@@ -97,17 +100,26 @@
(fn [^Node node]
(.getToken node)))
-(defmethod parse-extern-node Token/VAR [node]
+;; handle named function case (i.e. goog.modules)
+;; function foo {}, the entire function is the node
+(defmethod parse-extern-node Token/FUNCTION [^Node node]
+ (when (> (.getChildCount node) 0)
+ (let [ty (get-var-info node)]
+ (doto
+ (cond-> (parse-extern-node (.getFirstChild node))
+ ty (-> first (annotate ty) vector))))))
+
+(defmethod parse-extern-node Token/VAR [^Node node]
(when (> (.getChildCount node) 0)
(let [ty (get-var-info node)]
(cond-> (parse-extern-node (.getFirstChild node))
ty (-> first (annotate ty) vector)))))
-(defmethod parse-extern-node Token/EXPR_RESULT [node]
+(defmethod parse-extern-node Token/EXPR_RESULT [^Node node]
(when (> (.getChildCount node) 0)
(parse-extern-node (.getFirstChild node))))
-(defmethod parse-extern-node Token/ASSIGN [node]
+(defmethod parse-extern-node Token/ASSIGN [^Node node]
(when (> (.getChildCount node) 0)
(let [ty (get-var-info node)
lhs (cond-> (first (parse-extern-node (.getFirstChild node)))
@@ -120,13 +132,24 @@
lhs))
[lhs]))))
-(defmethod parse-extern-node Token/NAME [node]
- (let [lhs (map symbol (string/split (.getQualifiedName node) #"\."))]
- (if (> (.getChildCount node) 0)
- (let [externs (parse-extern-node (.getFirstChild node))]
- (conj (map (fn [ext] (concat lhs ext)) externs)
- lhs))
- [lhs])))
+;; JavaScript name
+;; function foo {}, in this case the `foo` name node
+;; {"foo": bar}, in this case the `bar` name node
+(defmethod parse-extern-node Token/NAME [^Node node]
+ (if (= Token/STRING_KEY (-> node .getParent .getToken))
+ ;; if we are inside an object literal we are done
+ []
+ ;; also check .getString - goog.module defs won't have qualified names
+ (let [name (or (.getQualifiedName node) (.getString node))
+ lhs (when-not (string/blank? name)
+ (map symbol (string/split name #"\.")))]
+ (if (seq lhs)
+ (if (> (.getChildCount node) 0)
+ (let [externs (parse-extern-node (.getFirstChild node))]
+ (conj (map (fn [ext] (concat lhs ext)) externs)
+ lhs))
+ [lhs])
+ []))))
(defmethod parse-extern-node Token/GETPROP [node]
(when-not *ignore-var*
@@ -135,6 +158,8 @@
(annotate props ty)
props)])))
+;; JavaScript Object literal
+;; { ... }
(defmethod parse-extern-node Token/OBJECTLIT [node]
(when (> (.getChildCount node) 0)
(loop [nodes (.children node)
@@ -144,8 +169,10 @@
(recur (rest nodes)
(concat externs (parse-extern-node (first nodes))))))))
-(defmethod parse-extern-node Token/STRING_KEY [node]
- (let [lhs (map symbol (string/split (.getString node) #"\."))]
+;; Object literal string key node
+;; {"foo": bar} - the key and value together
+(defmethod parse-extern-node Token/STRING_KEY [^Node node]
+ (let [lhs [(-> node .getString symbol)]]
(if (> (.getChildCount node) 0)
(let [externs (parse-extern-node (.getFirstChild node))]
(conj (map (fn [ext] (concat lhs ext)) externs)
@@ -154,7 +181,17 @@
(defmethod parse-extern-node :default [node])
-(defn parse-externs [^SourceFile source-file]
+(defn parse-externs
+ "Returns a sequential collection of the form:
+
+ [[foo core first]
+ [foo core next]
+ [foo core baz last] ...]
+
+ Where the last symbol is annotated with var info via metadata. This simple
+ structure captures the nested form of Closure namespaces and aids
+ direct indexing."
+ [^SourceFile source-file]
(binding [*source-file* (.getName source-file)]
(let [^CompilerOptions compiler-options
(doto (CompilerOptions.)
@@ -167,8 +204,13 @@
compiler)
(.init (list source-file) '() compiler-options))
js-ast (JsAst. source-file)
- ^Node root (.getAstRoot js-ast closure-compiler)]
- (loop [nodes (.children root)
+ ^Node root (.getAstRoot js-ast closure-compiler)
+ nodes (.children root)]
+ (loop [nodes (cond-> nodes
+ ;; handle goog.modules which won't have top-levels
+ ;; need to look at internal children
+ (= Token/MODULE_BODY (some-> nodes first .getToken))
+ (-> first .children))
externs []]
(if (empty? nodes)
externs
@@ -215,17 +257,42 @@
(= (inc (count ns-segs)) (count var-segs))
(= ns-segs (take (count ns-segs) var-segs)))))
-(defn parsed->defs [externs]
- (let [ns-segs (into [] (map symbol (string/split (str *goog-ns*) #"\.")))]
- (reduce
- (fn [m xs]
- ;; ignore definitions from other provided namespaces not under consideration
- (if (ns-match? ns-segs xs)
- (let [sym (last xs)]
- (cond-> m
- (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym}))))
- m))
- {} externs)))
+(defmulti parsed->defs (fn [_ module-type] module-type))
+
+(defmethod parsed->defs :goog
+ ([externs _]
+ (let [grouped (group-by #(= 'exports (first %)) externs)
+ exports (->> (get grouped true)
+ (map (comp vec rest))
+ (remove empty?)
+ set)
+ exported (filter exports (get grouped false))]
+ (reduce
+ (fn [m xs]
+ (let [sym (last xs)]
+ (cond-> m
+ (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym})))))
+ {} exported))))
+
+(defmethod parsed->defs :default
+ ([externs _]
+ (let [ns-segs (into [] (map symbol (string/split (str *goog-ns*) #"\.")))]
+ (reduce
+ (fn [m xs]
+ ;; ignore definitions from other provided namespaces not under consideration
+ (if (ns-match? ns-segs xs)
+ (let [sym (last xs)]
+ (cond-> m
+ (seq xs) (assoc sym (merge (meta sym) {:ns *goog-ns* :name sym}))))
+ m))
+ {} externs))))
+
+(defn resource->source-file
+ [resource]
+ (-> (SourceFile/builder)
+ (.withPath (.toPath (io/file (.getPath resource))))
+ (.withContent (io/input-stream resource))
+ (.build)))
(defn analyze-goog-file
([f]
@@ -237,11 +304,8 @@
(binding [*goog-ns* ns]
{:name ns
:defs (parsed->defs
- (parse-externs
- (-> (SourceFile/builder)
- (.withPath (.toPath (io/file (.getPath rsrc))))
- (.withContent (io/input-stream rsrc))
- (.build))))}))))
+ (parse-externs (resource->source-file rsrc))
+ (:module desc))}))))
(comment
(require '[clojure.java.io :as io]
diff --git a/src/test/cljs/cljs/predicates_test.cljs b/src/test/cljs/cljs/predicates_test.cljs
index 2e49406c5..acb50f4b0 100644
--- a/src/test/cljs/cljs/predicates_test.cljs
+++ b/src/test/cljs/cljs/predicates_test.cljs
@@ -8,7 +8,7 @@
(ns cljs.predicates-test
(:require [cljs.test :as test :refer-macros [deftest is]])
- (:import [goog.math Long Integer]))
+ (:import [goog.math Integer]))
(def pred-val-table
(let [uuid (uuid "00000000-0000-0000-0000-000000000000")]
@@ -41,9 +41,15 @@
(let [posint 10e10
negint -10e10
neg0 (/ ##-Inf)
- natl (Long.getZero)
- posl (Long.fromNumber posint)
- negl (Long.fromNumber negint)
+ ;; NOTE: we must go through a var because in self-parity tests
+ ;; we cannot simply import goog.module namespaces if cljs.core
+ ;; depends on the type - that's because cljs.core was *separately
+ ;; compiled* already bundling goog.modules. In many cases this is
+ ;; not an issue, but it is an issue if internally we use the type
+ ;; to make instanceof assertions - which we do for Long
+ natl (.getZero LongImpl)
+ posl (.fromNumber LongImpl posint)
+ negl (.fromNumber LongImpl negint)
nati Integer.ZERO
posi (Integer.fromNumber posint)
negi (Integer.fromNumber negint)]
@@ -69,4 +75,4 @@
(let [v (first row)]
(dotimes [i (count row)]
(is (= ((nth preds i) v) (nth row i))
- (pr-str (list (nth preds i) v))))))))
\ No newline at end of file
+ (pr-str (list (nth preds i) v))))))))
diff --git a/src/test/clojure/cljs/analyzer/glib_module_test.clj b/src/test/clojure/cljs/analyzer/glib_module_test.clj
new file mode 100644
index 000000000..ad4d126ea
--- /dev/null
+++ b/src/test/clojure/cljs/analyzer/glib_module_test.clj
@@ -0,0 +1,62 @@
+(ns cljs.analyzer.glib-module-test
+ (:require [cljs.analyzer :as ana]
+ [cljs.analyzer-tests :as ana-tests]
+ [clojure.test :as test :refer [deftest is testing]]
+ [cljs.env :as env]))
+
+(deftest glib-module-detect-test
+ (testing "Basic glib module detection"
+ (is (= :goog (get-in @ana-tests/test-cenv [:js-dependency-index (munge "goog.module.ModuleLoader") :module])))))
+
+(deftest glib-module-predicate-test
+ (testing "glib module detection predicate"
+ (env/with-compiler-env ana-tests/test-cenv
+ (is (ana/goog-module-dep? 'goog.module.ModuleLoader)))))
+
+(deftest glib-module-classification-test
+ (testing "glib module classification"
+ (env/with-compiler-env ana-tests/test-cenv
+ (is (= :goog-module (ana/ns->module-type 'goog.module.ModuleLoader))))))
+
+(deftest glib-module-resolve-var-test
+ (testing "glib module var resolution"
+ (let [cenv (env/default-compiler-env)
+ ns-ast (ana-tests/analyze-forms cenv
+ '[(ns foo.core
+ (:require [goog.module.ModuleLoader :as module-loader]))])
+ aenv (assoc (ana/empty-env) :ns (ana/get-namespace cenv 'foo.core))]
+ (is (= '{:name foo.core/goog$module$goog$module$ModuleLoader.EventType
+ :ns foo.core
+ :op :var}
+ (env/with-compiler-env cenv
+ (ana/resolve-var aenv 'module-loader/EventType)))))))
+
+(deftest glib-module-resolve-import-test
+ (testing "glib module resolve import helper test"
+ (let [cenv (env/default-compiler-env)
+ ns-ast (ana-tests/analyze-forms cenv
+ '[(ns foo.core
+ (:require [goog.module.ModuleLoader :as module-loader]))])
+ aenv (assoc (ana/empty-env) :ns (ana/get-namespace cenv 'foo.core))]
+ (is (= 'foo.core.goog$module$goog$module$ModuleLoader
+ (env/with-compiler-env cenv
+ (ana/resolve-import aenv 'goog.module.ModuleLoader)))))))
+
+(deftest glib-module-resolve-import-var-test
+ (testing "glib module :import var resolution"
+ (let [cenv (env/default-compiler-env)
+ ns-ast (ana-tests/analyze-forms cenv
+ '[(ns foo.core
+ (:import [goog.module ModuleLoader]))])
+ aenv (assoc (ana/empty-env) :ns (ana/get-namespace cenv 'foo.core))]
+ (is (= '{:name foo.core.goog$module$goog$module$ModuleLoader
+ :ns goog.module ;; a bit odd, but doesn't matter, for emission we just :name
+ :op :var}
+ (env/with-compiler-env cenv
+ (ana/resolve-var aenv 'ModuleLoader)))))))
+
+(comment
+
+ (test/run-tests)
+
+ )
diff --git a/src/test/clojure/cljs/compiler/glib_module_test.clj b/src/test/clojure/cljs/compiler/glib_module_test.clj
new file mode 100644
index 000000000..c9813d209
--- /dev/null
+++ b/src/test/clojure/cljs/compiler/glib_module_test.clj
@@ -0,0 +1,21 @@
+(ns cljs.compiler.glib-module-test
+ (:require [cljs.compiler-tests :as comp-tests]
+ [cljs.env :as env]
+ [clojure.test :as test :refer [deftest is testing]]))
+
+(deftest test-glib-module-compile
+ (testing "glib modules compiled to Closure Compile expectations"
+ (let [src (env/with-compiler-env (env/default-compiler-env)
+ (comp-tests/compile-form-seq
+ '[(ns test.foo
+ (:import [goog.module ModuleLoader]))
+ (def module-loader (ModuleLoader.))]))]
+ (is (re-find #"goog\.require\('goog\.module\.ModuleLoader'\)" src))
+ (is (re-find #"test\.foo\.goog\$module\$goog\$module\$ModuleLoader = goog\.module\.get\('goog.module.ModuleLoader'\)" src))
+ (is (re-find #"test\.foo\.module_loader = \(new test\.foo\.goog\$module\$goog\$module\$ModuleLoader\(\)\)" src)))))
+
+(comment
+
+ (test/run-tests)
+
+ )
diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj
index cc6bd0136..7bcc44539 100644
--- a/src/test/clojure/cljs/externs_parsing_tests.clj
+++ b/src/test/clojure/cljs/externs_parsing_tests.clj
@@ -36,6 +36,11 @@
(comment
+ (externs/parse-externs
+ (externs/resource->source-file (io/resource "goog/object/object.js")))
+
+ (externs/analyze-goog-file "goog/object/object.js")
+
(test/run-tests)
(externs/analyze-goog-file "goog/date/date.js" 'goog.date.month)