Fork me on GitHub
#shadow-cljs
<
2024-02-26
>
Shahwarcoder05:02:29

Hello Everyone! I am working on a project and am trying to setup code splitting using modules. I have create multiple modules that are dependent on each other but when I create the final optimised build, I see my Main.js file has a huge crore.cljs file of ~400KB, and I see that it has included dependencies for the lazy loaded components too. shouldn't it only load dependencies for the main module and rest should be loaded later on demand? Adding the screenshot of my report and my shadow.cljs file code below.

;; shadow-cljs configuration
{
 #_ (deps true allows shadow CLJS to load the dependencies
			from deps.edn file "")
 :deps true
 :dev-http {3000 "public"}
 :nrepl {:port 5432}
 :builds
 {:base-app
	{:target :browser
	 :module-loader true
	 :modules {:main {:entries [base-app.core]
										:init-fn base-app.core/init}
						 :data-table {:entries [fms.components.data-table]
													:depends-on #{:main}}
						 :fleet-create {:entries [fms.views.fleet.create]
													:depends-on #{:main}}
						 :login-page {:entries [auth.views.login]
													:depends-on #{:main}}
						 :home-page {:entries [home.views.home]
													:depends-on #{:main}}
						 :fms-base-page {:entries [fms.views.base]
												 :depends-on #{:home-page}}
						 :fms-fleet-list-page {:entries [fms.views.fleet]
														 :depends-on #{:home-page}}
						 }
	 :build-hooks [(shadow.cljs.build-report/hook)]
	 :compiler-options {:externs ["datascript/externs.js"]
											:optimizations :advanced
											}}}}

Shahwarcoder05:02:14

@U05224H0W Can you help how to optimise this and make the bundle size smaller?

thheller07:02:22

first you should figure out why cljs/analyzer.cljc is there. this should not be there unless its a self-hosted build

thheller07:02:55

otherwise I don't really have much to add to the above

thheller07:02:21

if you send me the full report I can make some suggestions, but going off screenshots this is going to take years

thheller07:02:14

the gist is that if everything depends on :main then ALL code the modules share can only go there

thheller07:02:31

and the screenshot you highlighted only shows which namespaces required cljs.core, which is all of them. so it is not a useful thing to look at and doesn't mean anything regarding splits

Shahwarcoder08:02:16

SO basically its like I have a main page, which lazily loads all other routes, hence I have added them as dependency. As also suggested here https://pitch-io.github.io/uix/docs/code-splitting.html

Shahwarcoder08:02:52

@U05224H0W here is the entire report file

thheller08:02:38

alright, that is much better to go off

thheller08:02:43

suggestion #1 to shrink the app: remove reitit or at the very least reitit-coercion. it brings in cljs.analyzer which it shouldn't

thheller08:02:20

if you scroll down you'll see that the :login-page module is tiny, as well as the :fleet-create

thheller08:02:41

the others look good, they are substantially larger and worth splitting out

Shahwarcoder08:02:06

Yes, that I saw, so the pages are small, I am using Reitit for routing, not sure how to remove it. Show is lazy load Reitit too?

thheller08:02:24

you cannot lazy load reitit and you don't need to remove all of it

thheller08:02:09

just the coercion part. I don't know what that is or how to remove it, but it contributes over 200kb to the build. I would remove it entirely due to that.

thheller08:02:47

frontend routing is not complicated, and IMHO reitit is complete overkill

thheller08:02:42

I cannot tell you how to remove it since I don't know what you are using it for

thheller08:02:19

besides those points the splits seem in good shape

Shahwarcoder08:02:21

I agree, I was thinking of using react-router but reitit was more idiomatic way suggested hence used it.

thheller08:02:14

sorry I cannot give you any advice on which router to use. I have not used anything react based for 5+ years

thheller08:02:10

if you only use reitit for routing then the build size it contributes is absolute overkill

thheller08:02:30

the way I treat "routing" is that there is a function that parses the URL path, and makes db data out of that. but I can't quite tell how you handle state, so that might not work in your system

thheller08:02:50

your build config has datascript/externs.js? but you don't appear to be using datascript?

thheller08:02:11

doesn't contribute to build size, so nothing to worry about. just something you can probably remove

Shahwarcoder08:02:17

yes, I just commented out Datascript to check how much size is reduced. the problem is, with time I will be using more of these libraries to complete my project. So I am not getting a clear idea of how to maintain my JS bundles small. Like datascript add 200KB to the app, now how will I add this library in future while maintaining the file size

thheller08:02:08

well yeah if you keep adding libraries it'll make your build larger

thheller08:02:24

especially for libraries that don't seem optimized for build size

thheller08:02:08

given that reitit is almost from 25% of your build I'd start there. for a router that seems excessive

Shahwarcoder08:02:19

so is the a way where we can lazy load npm modules, while the main.js app load initially with js and then these npm libraries are added when needed

thheller08:02:22

maybe you are doing something else with it that justifies that, can't tell

thheller08:02:33

npm modules are lazy loaded

thheller08:02:48

check the :home-page module, there is a lot of them

thheller08:02:07

the one in the :main module are used by multiple places, so they have to be there

thheller08:02:35

(and are also required by base-app.core directly for the most part, so cannot move elsewhere

thheller08:02:06

same for react/react-dom. they can't move because :main needs them

thheller08:02:06

but as I said the biggest chunk of :main that could potentially be removed is reitit and its dependencies

thheller08:02:10

I cannot see your code ... so I can only give you advice based on what I see in the report

thheller08:02:45

apart from the 2 tiny modules that don't really need to be modules it looks fine as far as splitting goes

thheller08:02:10

the rest is removing excessive stuff you likely do not need

Shahwarcoder08:02:16

@U05224H0W I understand reitit and will try to remove it. but why is core.cljs showing other modules also in the dependency.

Shahwarcoder08:02:01

If you want, I can share the code with you too, give me your github handle, I will add you to the repo

thheller08:02:09

I don't know what you mean, your screenshot is showing two lines without context of where they are

thheller08:02:44

if you mean the mouseover you get if you go over cljs/core.cljs for example

thheller08:02:55

it shows you what entry namespaces ended up requiring this

Shahwarcoder08:02:16

Yes, That's what I am speaking, when I hover I see all the lazy loaded modules.

thheller08:02:43

yes, this is fine. it does not mean they are in the :main module. it tells you why they are in the build at all

thheller08:02:17

so check cljs/analyzer.cljc, you'll see that it comes in via reitit, which was required by your base-app.core

jmckitrick13:02:04

Does :loader-mode have a function anymore in shadow-cljs.edn?

thheller14:02:27

it does yes

thheller14:02:39

:eval is the default, :script is the alternate

jmckitrick14:02:02

Looking for docs….

thheller14:02:26

:devtools {:loader-mode :script} in the build config. only ever had any effect in the :browser target

thheller14:02:46

why are you looking for it?

jmckitrick14:02:15

I’m cleaning up our build configs, and trying to consolidate and remove cruft or obsolete stuff.

jmckitrick14:02:39

Does :devtools belong on the same level as :dev or nested inside it?

thheller14:02:37

doesnt really matter, both work

thheller14:02:20

:dev is just merged into the main build config for dev builds

thheller14:02:43

the code only looks for :devtools, but thats where it ends up regardless

thheller14:02:53

if you want to share the build config I can give you feedback

jmckitrick14:02:01

That would be great, thank you. Just here in this thread?

jmckitrick15:02:07

:builds {:mp360 {:target :browser :js-options {:resolve {“moment” false ##“react” {:target :npm :require “preact/compat”} ##“react-dom” {:target :npm :require “preact/compat”} “jquery” {:target :global :global “jQuery”}} :ignore-asset-requires true} :devtools {:preloads [http://doublethedonation.dev day8.re-frame-10x.preload.react-18 devtools.preload] ;;:loader-mode :eval :after-load http://doublethedonation.integrations.app/dev-reload! ##:console-support false} :dev {:compiler-options {:closure-defines {re-frame.trace.trace-enabled? true day8.re-frame.tracing.trace-enabled? true}}} :release {:build-options {:ns-aliases {day8.re-frame.tracing day8.re-frame.tracing-stubs}}} :modules {:mp360 {:entries [http://doublethedonation.integrations.app]}} :output-dir “resources/public/js” :asset-path “/api/js” :build-hooks [(shadow.cljs.build-report/hook {:output-to “mp360_report.html”})] :compiler-options {:infer-externs :auto :closure-defines {re-frame.trace.trace-enabled? true day8.re-frame.tracing.trace-enabled? true}}}

jmckitrick15:02:18

Here’s the relevant bit I’m working on.

thheller15:02:14

the :compiler-options at the top level + dev is a bit redundant

jmckitrick15:02:17

ok cool, thank you. I was hoping to remove anything that just cluttered it up.

jmckitrick15:02:28

Yeah, I thought so as well.

thheller15:02:29

since I suspect you want to disable tracing for release

jmckitrick15:02:05

Correct. The tracing is new, I asked the CTO if I could roll it into our build file so I don’t have to stash it locally, since I’m the one that uses it most often.

jmckitrick15:02:01

Really all that’s left are loader-mode, autoload, and enabled.

thheller15:02:52

:loader-mode you can generally remove unless you have a very specific reason you are setting it to :script

thheller15:02:06

:eval has been the default for many years, so no need to set that

thheller15:02:18

:autoload disables hot-reload basically

thheller15:02:30

:enabled disables everything, so REPL and hot-reload

jmckitrick15:02:07

Ok I figured, because we have a separate ‘plugin’ build I did not include that is not a SPA, so hot-reload doesn’t make sense.

jmckitrick15:02:58

The takeaway is I can remove :loader-mode and consolidate :compiler-options and everything else is good to go.

souenzzo14:02:55

I'd like to find every access to JS globals js/* in my codebase. Does shadow-cljs helps me with that? somewhere in build report?

thheller14:02:00

build report no, but the build-state knows

thheller14:02:40

although whats wrong with a simple grep for js/?

souenzzo14:02:10

i did grepping but I was a bit worried if my grep misses something. There is no syntax other than js/, right?!

thheller14:02:33

why do you care? 😛

thheller14:02:05

I mean what are you worried about

souenzzo15:02:13

I broke production this morning because of an misspell in js/ access laughcry I'm looking for alternatives to avoid this type of problem I think that #CHY97NXE2 may be my path Warn when a new js/* var-usage appear our jsBundleGlobals.js is deprecated and don't have new fields anyway

souenzzo15:02:24

anyway, thank you @U05224H0W