Fork me on GitHub
#shadow-cljs
<
2022-02-13
>
alex02:02:53

Hi there - I'm looking to implement server-side rendering and running into an issue because one of my NPM dependencies, firebaseui, utilizes the browser window object. When I require the dependency e.g. (:require ["firebaseui" :as firebaseui]) , shadow is unable to compile the build. Is there a way around this? Should I try to lazy-load the dependency so that I can check for js/window before bringing the dependency into the build? If so, is there a way to lazy-load the NPM module directly, or do I need to create a standalone module my-app.firebaseui that imports firebaseui , then lazily load my-app.firebaseui into the rest of my app? I'm going to try ^ and report back on how it goes

alex03:02:15

It seems like even if I add a check for js/window , then try to lazy-load a separate my-app.firebase module containing the firebaseui import, shadow-cljs is still unable to compile. I could definitely be doing something wrong here - would appreciate your input. Here's a simplified example of what I have:

;; shadow-cljs.edn

{:builds {:client {:module-loader true
                   :modules {:client {:entries [my-app.client]}
                             :firebaseui {:entries [my-app.firebaseui]
                                          :depends-on #{:client}}}}}

;;

(ns my-app.firebaseui
  (:require ["firebaseui" :as firebaseui])

(defn init-firebaseui [] ...)

;; 

(ns my-app.sign-in
  (:require [shadow.lazy :as lazy])

(when js/window 
  (lazy (lazy/loadable my-app.firebaseui/init-firebaseui) 
        (fn [init-firebaseui] (init-firebaseui))))

thheller08:02:19

I made an example a while ago how you could move host specific requires to a namespace that is dedicated to a specific host (eg. browser and then one for node)

thheller08:02:33

then you can have that require in the browser version but not the node

thheller08:02:20

so you'll need a second build regardless. :target :browser isn't meant to run on node

alex17:02:38

Interesting, thanks! I'll give that a go

alex22:02:03

If I have some lazy-loaded modules in the :target :browser build, my Node build doesn't know that those modules exist. However, the Clojurescript compiler is still going to build the dependency tree and attempt to load those (undefined in Node) modules. As a result, my :node build is failing to compile due to an error with macroexpanding shadow.lazy/loadable on namespaces that don't have a module mapping. I believe the :modules config is specific to :browser builds and isn't supported for :node builds, so this seems to be expected behavior How should I approach making those modules compatible / ignoring those modules for the Node build? Is that something I should also handle via host-specific namespaces?

thheller07:02:02

This is kinda hard to answer without knowing exactly how you are using all this and how you intend to do the server side render. You could use custom reader conditionals to eliminate any calls for shadow.lazy for example. https://shadow-cljs.github.io/docs/UsersGuide.html#_conditional_reading

alex15:02:18

Thanks for pointing me to that! I was hoping something like that was supported but apparently didn't search the docs extensively enough