Fork me on GitHub
Timofey Sitnikov11:04:05

Good Morning Clojurians, I am doing some cljs with shadow-cljs and my main.js file is 20 mega bytes and every time I update something it takes 30 seconds to re-compile, is that normal for cljs?


Too few details. 20MB should be normal during development if you use a lot of and/or heavy third-party libraries (probably NPM). But it's not normal for a release bundle. 30 seconds is normal for a release build or for the initial debug build. It's not normal to wait 30 seconds for an incremental compilation step during watch.


try #shadow-cljs and post your shadow-cljs.edn file

Timofey Sitnikov11:04:15

@U4P4NREBY my shadow-cljs.edn file:

{:deps   {:aliases [:dev]}
 :nrepl  {:port 9000}
 :builds {:main       {:target     :browser
                       :output-dir "resources/public/js/main"
                       :asset-path "/js/main"

                       :modules    {:main {:init-fn app.client/init}}
                       ;; guardrails checks only during dev
                       :dev {:compiler-options
                              {:guardrails {:emit-spec? true :throw? false}}}}

                       :devtools   {:after-load app.client/refresh
                                    :preloads   [com.fulcrologic.fulcro.inspect.preload app.development-preload]}}

          :test       {:target           :browser-test
                       :test-dir         "resources/public/js/test"
                       :ns-regexp        "-test$"
                       ;; static required for mocking to work
                       :compiler-options {:static-fns      false
                                          :external-config {:guardrails {:throw? true}}}
                       :devtools         {:http-port          8022
                                          :http-resource-root "public"
                                          :http-root          "resources/public/js/test"}}

          :ci-tests   {:target           :karma
                       :js-options       {:js-provider :shadow}
                       :compiler-options {:static-fns      false
                                          :external-config {:guardrails {:throw? true}}}
                       :output-to        "target/ci.js"
                       :ns-regexp        "-test$"}

          :workspaces {:target
                       :ns-regexp  "-(test|ws)$"
                       :output-dir "resources/public/workspaces/js"
                       :asset-path "/workspaces/js"
                       :devtools   {:preloads           [com.fulcrologic.fulcro.inspect.preload]
                                    :http-root          "resources/public/workspaces"
                                    :http-port          8023
                                    :http-resource-root "."}}}}


@U012GN57FJ9 If 20MB is the release build, then you can use this to understand what inflates the bundle:

Timofey Sitnikov11:04:19

No, 20 mb is development build.


Then it's perfectly normal.

Michaël Salihi13:04:51

Hi! I think I had confirmation that a dynamic import/require like in this example is not possible in CLJS (browser).

const app = document.getElementById('app');

    resolveComponent={name =>
      import(`./Pages/${name}`).then(module => module.default)
Someone would have an idea of an alternative solution? I would like to avoid calling Reagent components one by one by hand and have the same level of automatism as in the example given.

Michaël Salihi13:04:42

For now, here's how I do it.

(ns myreagent.core
  (:require ["@inertiajs/inertia-react" :refer [App]]
            [myreagent.pages.home :refer [Home]]
            [myreagent.pages.demo :refer [Demo]]
            [reagent.core :as r]
            [reagent.dom :as d]))

(def el (.getElementById js/document "app"))

(defn home-page []
  [:> App {:initial-page (.parse js/JSON (.. el -dataset -page))
           :resolve-component (fn [name] (r/reactify-component (case name
                                                                "Home" Home
                                                                "Demo" Demo)))}])

(defn mount-root []
  (d/render [home-page] el))

(defn ^:export init! []


Module splitting, probably. But use it sparsely and only for isolated chunks of functionality. I.e. there's very little reason to split the main page with 10 panels into 10 modules if all the panels will be displayed at the same time unconditionally anyway.

Joseph Rollins14:04:52

I'm having trouble using shadow-cljs. I have a basic build:

{:fn {:target :node-library
       :compiler-options {:infer-externs :auto}
       :output-to "functions/index.js"
       :exports-var functions.fn/exports}}
Yet it complains required namespace "functions.fn" is not available but I have src/functions/fn.cljs with:
(ns functions.fn
  (:require ["firebase-functions" :as functions]
            ["firebase-admin" :as admin]))

(defonce init (.initializeApp admin))

(defn echo [req res]
  (js/console.log req res))

(def exports
  #js {:echo (.onCall functions/https echo)})
I assume i'm missing something pretty trivial but I cannot figure it out. It was trivial -- I needed my file to be at src/main/functions/fn.cljs


Should't transit save metadata?

(write-transit (with-meta {:a 1} {:b 2}))
;; => "[\"^ \",\"~:a\",1]"
(meta (read-transit (write-transit (with-meta {:a 1} {:b 2}))))
;; => nil


The key is :transform write-meta


If you are also using transit-clj, watch out for this issue

Alex Miller (Clojure team)20:04:51

no, transit does not preserve metadata

Alex Miller (Clojure team)20:04:10

it's designed to be language agnostic and that's not a feature generally available


I found such a line in the code and I was convinced that it saves metadata

Alex Miller (Clojure team)20:04:00

looks like maybe the transit-clj / transit-cljs libs do have an optional transform for it

Alex Miller (Clojure team)20:04:53

in transit-cljs, takes a :transform, and write-meta is an available transform

💯 6
Alex Miller (Clojure team)20:04:06

so, I take it back, it does support it (if asked)!


@djblue already told me this solution in the thread


metadata is probably one of the most underrated feature

☝️ 3

Especially with how you can implement protocols via metadata!


I haven't tried this yet, but it sounds great.


It's particularly useful for implementing clojure.core.protocols/nav on your which allows tools to help you navigate through your data.