Fork me on GitHub
#shadow-cljs
<
2023-06-23
>
Ryan00:06:02

Hey all, seeing a new error in my shadow-cljs repl: INTERNAL REPL ERROR: Got an unexpected reply from relay, check Inspect when inspecting a re-frame app-db (it is a lot of data, like maybe 200k, but its never been a problem before). Any clues of places to start looking for trouble would be appreciated.

Ryan00:06:54

Aha, to those that may run into this.. the app hit a runtime error and the state of the JS was wonky. A reload fixed it up.

Sturm03:06:36

I'm currently using @heroicons/react for icons where I import with (:require ["@heroicons/react/24/outline" :as hero]) and then use it like [:div hero/ListBulletIcon] but the full 400k icon set ends up in my production build. I've been trying to import just an individual icon in case that helps but struggling. The JS looks like this:

// node_modules/@heroicons/react/24/outline/index.js
module.exports.ListBulletIcon = require("./ListBulletIcon.js")
...

// node_modules/@heroicons/react/24/outline/ListBulletIcon.js
const React = require("react");
function ListBulletIcon(...) {...}
const ForwardRef = React.forwardRef(ListBulletIcon);
module.exports = ForwardRef;
I've tried to import with (require '["@heroicons/react/24/outline/ListBulletIcon$default" :as ListBulletIcon]), but that obviously not quite right as ListBulletIcon is nil. Any suggestions? If I was able to get the import working, would it likely help the bundle size?

hifumi12303:06:05

If an ESM version exists, then try using that instead. IIRC the CJS modules are simply impossible to tree-shake

Sturm04:06:40

Thank you! After discovering that there was an esm subdirectory in that NPM package, I was able to require the icons individually with: (require ["@heroicons/react/24/outline/esm/ListBulletIcon$default" :as ListBulletIcon]) I also tried importing the whole ESM module to see if it was able to tree-shake that, but it wasn't. (require ["@heroicons/react/24/outline/esm" :as hero]) Thanks again for the tip @U0479UCF48H - I would never have found that "esm" directory!

hifumi12304:06:18

no problem… also shadow-cljs lets you write default imports with a :default keyword

hifumi12304:06:44

e.g.

(require '["@heroicons/react/24/outline/esm/ListBulletIcon" :default ListBulletIcon])

Sturm04:06:57

brilliant, thanks!

thheller04:06:37

FWIW consider :default deprecated. the official way to access default exports is "@heroicons/react/24/outline/esm/ListBulletIcon$default"

thheller04:06:44

when you mouseover a line it'll tell you what ended up including it

Sturm04:06:06

yeah, that build report is brilliant - I really appreciate that

thheller04:06:23

but you seem to have found your solution so thats good

thheller04:06:25

ESM or commonjs doesn't really matter though

thheller04:06:34

shadow-cljs does not treeshake npm dependencies

👍 4
Sturm05:06:22

Just shaved about 600KB of JS off my home page thanks to all the above!

👍 4
hifumi12308:06:40

Does setting :jvm-opts in shadow-cljs allow us to increase the stack size when invoking google-closure?

thheller08:06:38

well, it does allow you to increase the stack size yes. just for everything, not just closure

hifumi12308:06:51

That’s exactly what I need, thanks. As you can probably guess, it’s that stupid highlight.js file nobody ever hears of until java throws a StackOverflowException in CI builds…

thheller08:06:44

well you should just not include it in the first place

thheller08:06:16

not only is it crashing the build, it'll also bloat your build needlessly since those files are quite big and probably unused 😉

thheller08:06:53

unless you don't care about build size, then the stack size is the easy fix

hifumi12308:06:52

Yeah, my specific case involves a private deployment where bundle size is not a concern. I tried using custom resolves in :js-options at first, but the file was still somehow being included, and build reports weren’t making it clear where this file is being required. So for now, I’m taking the stack size option and making a note to get rid of the one thing in my codebase requiring highlight.js

scarytom12:06:10

Getting shadow-cljs - failed to load module$node_modules$$js_joda$locale_en$dist$index when attempting to use js-joda formatting https://js-joda.github.io/js-joda/manual/formatting.html Any ideas?

thheller12:06:36

why does it fail to load? there should be more info on that error

scarytom12:06:24

portal.js:1288 TypeError: Cldr.load is not a function
    at new WeekFields (index.js:317213:12)
    at WeekFields.ofFirstDayOfWeekMinDays (index.js:317193:17)
    at WeekFields.of (index.js:317167:27)
    at eval (index.js:317255:33)
    at eval (index.js:8:66)
    at shadow$provide.module$node_modules$$js_joda$locale_en$dist$index (index.js:7:1)
    at shadow.js.jsRequire (js.js:66:18)
    at shadow.js.require (js.js:113:20)
    at eval (blue.strategic.porta…shared.time.js:3:65)
    at eval (<anonymous>)

thheller12:06:53

no clue. might be a version conflict?

scarytom12:06:45

I'm using the latest version of all the js-joda packages though

scarytom12:06:53

the strange thing is this all works fine for the unit tests, whose target is :node-test as opposed to :browser

thheller12:06:03

could just be that node is loading different dependencies than shadow-cljs is trying to bundle

thheller12:06:22

you can setup a repo so I can take a look

thheller12:06:34

but without more details I can't really make any suggestions

scarytom12:06:01

I'll set up a repo.

scarytom13:06:35

turns out it reproduces trivially on your browser-quickstart example project

scarytom13:06:57

All I did was

npm install @js-joda/core
npm install @js-joda/timezone
npm install @js-joda/locale_en
and then added these imports to starter.browser:
(:require
    ["@js-joda/core" :as joda]
    ["@js-joda/locale_en" :refer [Locale]]
    ["@js-joda/timezone"])

scknkkrer12:06:38

Hey fellas, Is there any working example of full-stack ClojureScript application to demonstrate Server-Side Rendering (SSR)? When I try to import my view, it gives me an error:

import React, { useContext, useRef, useEffect, createElement, useState, useMemo, Fragment, useCallback } from 'react';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at internalCompileFunction (node:internal/vm:73:18)
    at wrapSafe (node:internal/modules/cjs/loader:1175:20)
    at Module._compile (node:internal/modules/cjs/loader:1219:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1309:10)
    at Module.load (node:internal/modules/cjs/loader:1113:32)
    at Function.Module._load (node:internal/modules/cjs/loader:960:12)
    at Module.require (node:internal/modules/cjs/loader:1137:19)
    at require (node:internal/modules/helpers:121:18)
    at /Users/sckn/projects/closed-source/lunara/cls/.shadow-cljs/builds/lunara.cls.backend/dev/out/cljs-runtime/shadow.js.shim.module$$ionic$react.js:3:38
    at global.SHADOW_IMPORT (/Users/sckn/projects/closed-source/lunara/cls/target/backend/main.js:64:44)

(comment
  ;;;server-side rendering:
  (reagent.dom.server/render-to-string [c/application]))
All I did is importing the namespace.

thheller12:06:30

that is a node error

scknkkrer12:06:05

Yeap. I wanna render my whole application inside my nodejs runtime (Shadow-CLJS Node application).

thheller12:06:21

can't really help you, this is a node error. could be that the packages are just not compatible with node?

thheller12:06:54

you can try setting :js-options {:js-provider :shadow :keep-native-requires true} in the build config, that'll make shadow-cljs bundle more than it usually would for node builds

thheller12:06:11

no guarantee that does anything useful though

thheller12:06:19

could try using :target :esm as well, but thats another story full of speedbumps

scknkkrer12:06:25

Let’s say I wanna render some piece of React in Back-End with Shadow-CLJS, what is the highly-overview steps of this in order to render it to string, so handler can send it?

Mario Trost19:06:59

@scknkkrer I think #sitefox does something along these lines, according to the Reamde: https://github.com/chr15m/sitefox/blob/main/README.md#templates (edited)

Mario Trost19:06:39

It does use react through reagent, don't know if you want to use react directly

scknkkrer12:06:29

Hmm, let me try.

thheller12:06:53

I don't know. I don't use react or node as my server 😛

thheller12:06:05

maybe ask in #C0620C0C8 or so, people over there probably have tried

scknkkrer12:06:43

Thanks for the help, so far @thheller. I was really looking deep to your works these days. Thanks for everything man!

scknkkrer13:06:01

Is it really possible to hook-up with Webpack for NodeJS target?

Ted Ciafardini14:06:55

I have an optimization question - If a namespace is declared as such:

[devcards.core :as dc :refer [defcard defcard-rg]
and the symbols defcard & defcard-rg are used, but not the alias dc - Is it possible that not using the alias dc/ will lead to the namespace being ignored while doing a release build?

thheller16:06:17

not sure I understand the question

thheller16:06:23

what do you mean by "ignored"?

thheller16:06:37

I'm pretty sure devcards defaults to not emitting anything when optimizations are enabled

thheller16:06:48

but thats a devcards thing and not related to shadow-cljs in any way

Ted Ciafardini16:06:29

The devcards weren’t ending up rendering - but when I wrote out the namespace declarations very explicitly (not including :as dc or un-used symbols, they did render.

Ted Ciafardini16:06:39

They didn’t end up in the compiled javascript

thheller16:06:43

that is definitely of no consequence whatsoever

Ted Ciafardini16:06:54

seems like it shouldn’t be

thheller16:06:30

from this I'd say devcards only emits stuff if :devcards true is set in the build

thheller16:06:50

or the closure-define is set

Ted Ciafardini16:06:51

this happens with :devcards set to true,

Ted Ciafardini16:06:59

it’s really odd

thheller16:06:59

where or how do you set it?

Ted Ciafardini16:06:06

:compiler-options

thheller16:06:28

is that maybe in a :dev {:compiler-options {:devcards true}}} nesting?

Ted Ciafardini16:06:28

it works now, but only because I changed the require for namespaces where the cards were defined

Ted Ciafardini16:06:35

{:builds
 {:app
  {:target :browser
   :compiler-options {:devcards true}}}}
abbreviated

thheller16:06:47

to answer your question: no, it is not possible that not using :as dc causes code to be removed/unused. what however is possible is that the code is removed if nothing is actually used from that namespace. it is also a possibility that the code just gets moved/inlined if its only used once/a few times.

thheller16:06:25

:advanced output can be tricky to analyze manually, but I assure you that :as dc has no effect whatsoever on the build output

Eitan16:06:52

Hello everyone, I have two different builds, one for development and one for testing. In my development build I have some devtools, but I remove those in my test build. I expect the tests to run without the devtools, but the tools end up appearing. I thought separating the builds would prevent this. Is there something I am misunderstanding about constructing builds?

thheller16:06:21

need more details about the setup to answer. in general the build will include whatever is required. so if any of your namepaces require those devtools directly, then they'll be part of the build

Eitan17:06:33

Sorry about not giving enough detail. It turns out that I was looking in the wrong directory for the compiler output. Thank you for responding.

hifumi12319:06:20

@scknkkrer I have a basic example of some server-side rendered react apps, but with Helix, not with Reagent. The idea is to just use whatever React already provides OOTB for rendering. I don’t think it’s possible to use SSR with reagent since it requires running an interpreter to dynamically create react elements, and it is almost certainly going to use react client API, not react server API. I’m probably wrong though since I’ve only ever done Node SSR with Helix

scknkkrer20:06:10

Hello @U0479UCF48H, Is it open-source, can I inspect your example? Also, is it an example or a POC, to be specific?

hifumi12321:06:12

not open source, yet, and I was still trying to get emotion to server-side render CSS together with react, but havent gotten it working yet with that said, i can server-side render material ui components and use macchiato-core + a shared reitit router to resolve what pages to pre-render and serve

scknkkrer23:06:25

Are you using re-frame to handle state? Because I’ve just found out a really good flaw in it’s design. It can’t handle async. It uses a shared resources to handle things.

hifumi12300:06:29

State is handled with refx instead. I follow a similar approach to SSR with Redux, where you share an initial app-db across client and server code and the server renders whatever it can with the initial app-db. Then when client-side loads JS, initialize app-db, hydrate, and continue rendering from there

hifumi12300:06:45

You can probably get away with dispatching events on the server-side and embedding the app-db into the rendered HTML, but I haven’t tried this yet. For now I’ve followed an implicit rule where I do not use reitit’s route interceptors at all in this app, so that I can avoid having to modify the app-db as long as possible. Likewise, the routing data is stored outside of the app-db and instead in react context.

rads16:06:57

> I don’t think it’s possible to use SSR with reagent since it requires running an interpreter to dynamically create react elements, and it is almost certainly going to use react client API, not react server API. One way to get SSR in Reagent/Re-frame without a Node.js runtime is to write components in CLJC and use a Reagent-flavored Hiccup library on the server. Then you use the same "client-side loads JS, initialize app-db, hydrate, and continue rendering from there" approach. I'm hoping to write a blog post on how to do this sometime soon

rads16:06:17

It's a bit different way of thinking but it allows me to improve the First Content Paint of a Reagent/Re-frame app using only a JVM-based shadow-cljs setup. I can also export my Reagent/Re-frame app as a static site using only the JVM REPL

rads02:06:10

Here's an example I just finished working on for SSR with Reagent/Re-frame without a Node.js runtime: https://github.com/rads/rain.examples.todomvc https://todomvc.rads.dev