Fork me on GitHub
#shadow-cljs
<
2020-11-22
>
lambdam10:11:44

Hello everyone, I have a project where I want to have many different CLJS entry points. Instead of having one big webapp, I have a lot of small webapps. If I understand well the docs, the :modules functionality is meant for on big webapp that would be splitted into various chunks. Is it possible instead to have many entry points that would share a common set of namespaces? Thanks.

robert-stuttaford10:11:54

how do you want code to be shared? do you want all the code in each app's js, or do you want there to be a common js file with all the common bits, and then smaller app js files for each app? you can do big-app-with-lazy-modules and lots-of-apps-that-share-code with :modules; the difference is in how you decide to load code via script tags (one for a big app, or different ones on different pages for lots of apps) / loading GClosure modules on demand (in your big app)

robert-stuttaford10:11:52

is there a way to add a deps.edn dependency and have shadow-cljs load it without having to restart the compiler?

thheller11:11:02

@dam :modules allows you to split the code so you only load whatever is needed. if you instead want everything in one file and just use whatever from it you can use (defn ^:export foo []) and then call <script>your.ns.foo();</script> from the HTML

thheller11:11:26

ie. each exported function can be your entry point

lambdam12:11:59

Thanks for your answers. I found a workaround that worked for my need. I have it working for dev and prod with the hashed names (for cache buster).

;; shadow-cljs.edn

{:deps true
 :builds {:app {:target :browser
                :output-dir "resources/public/cljs"
                :asset-path "/cljs"
                :release {:output-dir "resources/public/cljs"
                          :module-hash-names 12
                          :build-options {:manifest-name "assets.edn"}}
                :modules {:shared-deps {:entries [myapp.front.shared-deps]}
                          :entry-point-a {:init-fn myapp.front.entry-point-a/main!
                                          :depends-on #{:shared-deps}}
                          :entry-point-b {:init-fn myapp.front.entry-point-b/main!
                                          :depends-on #{:shared-deps}}
                          :entry-point-c {:init-fn myapp.front.entry-point-c/main!
                                          :depends-on #{:shared-deps}}
                          }}}}

;; myapp/front/shared_deps.cljs

(ns myapp.front.shared-deps)

(defn foo [])

;; myapp/lib/compile_time_util.clj

(ns myapp.lib.compile-time-util
  (:require [clojure.edn :as edn]
            [ :as io]))

(defmacro inject-cljs-resource [resource-id]
  (let [env (System/getenv "MYAPP_ENV")
        cljs-dir "public/cljs"
        asset-path "/cljs"]
    (if (contains? #{"prod" "testing"} env)
      (->> (str cljs-dir "/assets.edn")
           io/resource
           slurp
           edn/read-string
           (some (fn [entry]
                   (when (= (:name entry) resource-id)
                     entry)))
           :output-name
           (str asset-path "/"))
      (str asset-path "/" (name resource-id) ".js"))))

;; myapp/server/views/mypage.clj

(defn render-page [req]
  (layouts/render-layout
    (assoc req
           ::vutil/extra-js-deps [(ct-util/inject-cljs-resource :shared-deps)
                                  (ct-util/inject-cljs-resource :entry-point-a)])
    [:div
     [:h1 "Title"]
     ...]))

👍 3
Rin A20:11:31

Hi, I have a bit of a noob question, sorry... I'm building a webapp, and I'm using shadow-cljs. I'm trying to use google firebase, so I tried npm i --save firebase, and it updates my package.json fine. The firebase docs say to require it in a normal js app using import firebase from "firebase/app"; so as per https://shadow-cljs.github.io/docs/UsersGuide.html#_using_npm_packages I've imported it using ["firebase/app" :default firebase]. But if I try to use that import with (firebase/initializeApp #js{ ... }) I get the following error: `No such namespace: firebase, could not locate firebase.cljs, firebase.cljc, or JavaScript source providing "firebase"` If I try to require it instead by using ["firebase/app" :as firebase] then if I try to use it with (firebase/initializeApp #js{ ... }) I get the following error instead: #object[TypeError TypeError: module$node_modules$firebase$app$dist$index_esm.initializeApp is not a function] If I look at node_modules/firebase/app/dist/index.esm.js it looked like it was just loading from @firebase. So I try ["@firebase/app" :as firebase] and I get #object[TypeError TypeError: module$node_modules$$firebase$app$dist$index_esm.initializeApp is not a function]. But if I look at node_modules/@firebase/app/dist/index.esm.js it seems like the function would be there. For the heck of it, and to see if I'm understanding how to do this properly, I try installing underscore and importing it as ["underscore" :as underscore] and that totally works. I can go into the REPL and run > (.entries js/Object underscore) and I'll get something that makes sense. But if I try > (.entries js/Object firebase) then I get something a little hard for me to explain:

The result object failed to print. It is available via *1 if you want to interact with it.
The exception was:
RangeError: Maximum call stack size exceeded
:shadow.cljs/print-error!
I think I've probably just made a silly error somewhere, but I don't seem quite capable of figuring it out - does anyone know what I've done wrong in installing and requiring firebase? Also I think this is a shadow-cljs thing but let me know if I'm wrong and this is totally the wrong place to ask...

thheller21:11:59

@ryielle :as sets up a namespace alias so you can do firebase/doX. :default does not setup up an alias but instead acts like :refer. so you can do (.initializeApp firebase) with :default firebase

Rin A23:11:52

Amazing, thank you so much for this!