       :dot-call
       (comp/emits f ".call(" (comma-sep (cons "null" args)) ")"))))
 
+;; fixes an issue where the cljs.core variant assumes that `resolve-var` will only return
+;; a js/foo symbol if x is also js/something which isn't true in many cases for shadow-cljs
+;; where npm deps especially resolve some/foo to js/module$something.foo
+
+;; https://github.com/clojure/clojurescript/blob/f884af0aef03147f3eef7a680579f704a7b6b81c/src/main/clojure/cljs/core.cljc#L991
+;; this introduces the new "resolved" and uses that in the following cond-> as opposed to testing the js namespace on x only
+(defn shadow-exists?
+  "Return true if argument exists, analogous to usage of typeof operator
+   in JavaScript."
+  [&form &env x]
+  (if (symbol? x)
+    (let [resolved (:name (ana/resolve-var &env x))
+          y (cond-> resolved
+              (= "js" (namespace resolved)) name)
+          segs (str/split (str (str/replace (str y) "/" ".")) #"\.")
+          n (count segs)
+          syms (map
+                 #(vary-meta (symbol "js" (str/join "." %))
+                    assoc :cljs.analyzer/no-resolve true)
+                 (reverse (take n (iterate butlast segs))))
+          js (str/join " && " (repeat n "(typeof ~{} !== 'undefined')"))]
+      (-> (concat (list 'js* js) syms)
+          ;; cljs.core/bool-expr is private ...
+          (vary-meta assoc :tag 'boolean)))
+    `(some? ~x)))
+
 (defn install-hacks! []
   ;; cljs.analyzer tweaks
   (replace-fn! #'ana/load-core shadow-load-core)

                    (.concat (array ~this-sym) (cljs.core/aclone ~argsym)))))
             (meta form)))]
      (ifn-invoke-methods type type-sym form))))

;; not a super critical issue but foo may resolve to js/module$foo...
;; which ends up emitting a bad code check
;; if((typeof js !== 'undefined') && (typeof js.module$foo !== 'undefined')){
;; temp fix till I can sort it out in resolve-var directly
(core/defmacro exists?
  "Return true if argument exists, analogous to usage of typeof operator
   in JavaScript."
  [x]
  (if (core/symbol? x)
    (core/let [resolved (:name (cljs.analyzer/resolve-var &env x))
               y (core/cond-> resolved
                   (= "js" (namespace resolved)) name)
               segs (string/split (core/str (string/replace (core/str y) "/" ".")) #"\.")
               n (count segs)
               syms (map
                      #(vary-meta (symbol "js" (string/join "." %))
                         assoc :cljs.analyzer/no-resolve true)
                      (reverse (take n (iterate butlast segs))))
               js (string/join " && " (repeat n "(typeof ~{} !== 'undefined')"))]
      (bool-expr (concat (core/list 'js* js) syms)))
    `(some? ~x)))

(comment

  (core/defmacro defprotocol
    "A protocol is a named set of named methods and their signatures:

    (defprotocol AProtocolName
      ;optional doc string
      \"A doc string for AProtocol abstraction\"

    ;method signatures
      (bar [this a b] \"bar docs\")
      (baz [this a] [this a b] [this a b c] \"baz docs\"))

    No implementations are provided. Docs can be specified for the
    protocol overall and for each method. The above yields a set of
    polymorphic functions and a protocol object. All are
    namespace-qualified by the ns enclosing the definition The resulting
    functions dispatch on the type of their first argument, which is
    required and corresponds to the implicit target object ('this' in
    JavaScript parlance). defprotocol is dynamic, has no special compile-time
    effect, and defines no new types.

    (defprotocol P
      (foo [this])
      (bar-me [this] [this y]))

    (deftype Foo [a b c]
      P
      (foo [this] a)
      (bar-me [this] b)
      (bar-me [this y] (+ c y)))

    (bar-me (Foo. 1 2 3) 42)
    => 45

    (foo
      (let [x 42]
        (reify P
          (foo [this] 17)
          (bar-me [this] x)
          (bar-me [this y] x))))
    => 17"
    [psym & doc+methods]
    (core/let [p (:name (cljs.analyzer/resolve-var (dissoc &env :locals) psym))
               [opts methods]
               (core/loop [opts {:protocol-symbol true}
                           methods []
                           sigs doc+methods]
                 (core/if-not (seq sigs)
                   [opts methods]
                   (core/let [[head & tail] sigs]
                     (core/cond
                       (core/string? head)
                       (recur (assoc opts :doc head) methods tail)
                       (core/keyword? head)
                       (recur (assoc opts head (first tail)) methods (rest tail))
                       (core/list? head)
                       (recur opts (conj methods head) tail)
                       :else
                       (throw #?(:clj  (Exception.
                                         (core/str "Invalid protocol, " psym " received unexpected argument"))
                                 :cljs (js/Error.
                                         (core/str "Invalid protocol, " psym " received unexpected argument"))))
                       ))))
               psym (vary-meta psym merge opts)
               ns-name (core/-> &env :ns :name)
               fqn (core/fn [n] (symbol (core/str ns-name) (core/str n)))
               prefix (protocol-prefix p)
               _ (core/doseq [[mname & arities] methods]
                   (core/when (some #{0} (map count (filter vector? arities)))
                     (throw
                       #?(:clj  (Exception.
                                  (core/str "Invalid protocol, " psym
                                            " defines method " mname " with arity 0"))
                          :cljs (js/Error.
                                  (core/str "Invalid protocol, " psym
                                            " defines method " mname " with arity 0"))))))
               expand-sig (core/fn [fname slot sig]
                            (core/let [sig (core/if-not (every? core/symbol? sig)
                                             (mapv (core/fn [arg]
                                                     (core/cond
                                                       (core/symbol? arg) arg
                                                       (core/and (map? arg) (core/some? (:as arg))) (:as arg)
                                                       :else (gensym))) sig)
                                             sig)

                                       fqn-fname (fqn fname)
                                       fsig (first sig)

                                       impl-sym (with-meta (gensym "impl_fn") {:tag 'function})

                                       ;; last check protocol on js null,string,function,array,object or default
                                       ;; this is identical for all impls so handled in a helper fn
                                       ;; default impls are registered via (goog.object/set protocol-fn "string" (fn [...] ...))
                                       ;; invoke still happens here since the arity varies
                                       check
                                       `(let [~impl-sym (js/shadow.cljs_helpers.find_protocol_impl ~fqn-fname ~fsig)]
                                          (if-not (nil? ~impl-sym)
                                            (~impl-sym ~@sig)
                                            (throw
                                              (missing-protocol
                                                ~(core/str psym "." fname) ~fsig))))

                                       ;; FIXME: this should actually be the first check, but that requires rewriting direct protocol invoke too
                                       ;; then check protocol fn in metadata (only when protocol is marked with :extend-via-metadata true)
                                       check
                                       (core/if-not (:extend-via-metadata opts)
                                         check
                                         `(if-let [meta-impl# (-> ~fsig (core/meta) (core/get '~fqn-fname))]
                                            ;; FIXME: this will go through IFn property check, can this even be IFn? likely only function?
                                            (meta-impl# ~@sig)
                                            ~check))

                                       ;; check protocol property on object (first check actually executed)
                                       check
                                       `(if (and (not (nil? ~fsig))
                                                 (not (nil? (. ~fsig ~(symbol (core/str "-" slot)))))) ;; Property access needed here.
                                          (. ~fsig ~slot ~@sig)
                                          ~check)]
                              `(~sig ~check)))
               psym (core/-> psym
                             (vary-meta update-in [:jsdoc] conj "@interface")
                             (vary-meta assoc-in [:protocol-info :methods]
                               (into {}
                                 (map
                                   (core/fn [[fname & sigs]]
                                     (core/let [doc (core/as-> (last sigs) doc
                                                      (core/when (core/string? doc) doc))
                                                sigs (take-while vector? sigs)]
                                       [(vary-meta fname assoc :doc doc)
                                        (vec sigs)]))
                                   methods)))
                             ;; for compatibility with Clojure
                             (vary-meta assoc-in [:sigs]
                               (into {}
                                 (map
                                   (core/fn [[fname & sigs]]
                                     (core/let [doc (core/as-> (last sigs) doc
                                                      (core/when (core/string? doc) doc))
                                                sigs (take-while vector? sigs)]
                                       [(keyword fname) {:name fname :arglists (list* sigs) :doc doc}]))
                                   methods))))
               method (core/fn [[fname & sigs]]
                        (core/let [doc (core/as-> (last sigs) doc
                                         (core/when (core/string? doc) doc))
                                   sigs (take-while vector? sigs)
                                   amp (core/when (some #{'&} (apply concat sigs))
                                         (cljs.analyzer/warning
                                           :protocol-with-variadic-method
                                           &env {:protocol psym :name fname}))
                                   _ (core/when-some [existing (core/get (-> &env :ns :defs) fname)]
                                       (core/when-not (= p (:protocol existing))
                                         (cljs.analyzer/warning
                                           :protocol-with-overwriting-method
                                           {} {:protocol psym :name fname :existing existing})))
                                   slot (symbol (core/str prefix (munge (name fname))))
                                   fname (vary-meta fname assoc
                                           :protocol p
                                           :doc doc)]
                          `(defn ~fname
                             ~@(map (core/fn [sig]
                                      (expand-sig fname
                                        (symbol (core/str slot "$arity$" (count sig)))
                                        sig))
                                 sigs))))]
      `(do
         (set! ~'*unchecked-if* true)
         (def ~psym (~'js* "function(){}"))
         ~@(map method methods)
         (set! ~'*unchecked-if* false)))))


  (replace-fn! #'cljs.core/goog-define goog-define)
  (replace-fn! #'cljs.core/defonce shadow-defonce)

  (replace-fn! #'cljs.core/exists? shadow-exists?)

  ;; remove these for now, not worth the trouble
  ;; (replace-fn! #'test/deftest @#'shadow-deftest)
