Fork me on GitHub
#shadow-cljs
<
2019-11-26
>
lilactown00:11:12

we have a pretty large re-frame app at work

thheller00:11:03

try 2.8.76 and generate a build report and then one with :compiler-options {:shadow-keywords true} looking to compare the gzip size before/after

thheller00:11:39

impact will be bigger with lots of namespaced keywords though

thheller00:11:39

oops :compiler-options not :js-options

lilactown00:11:08

in our main bundle, • before: [JS: 4.19 MB] [GZIP: 1019.18 KB] • after: [JS: 3.99 MB] [GZIP: 960.59 KB]

thheller00:11:34

thats a decent chunk. thx.

thheller00:11:57

(scary large build btw)

lilactown00:11:00

PS we ship way too much code

lilactown00:11:18

it’s my goal in life right now to at least get code splitting going so we can chunk out the loading process better

lilactown00:11:30

on low-end machines it takes like 10s to download and load all the JS

thheller00:11:24

it'll probably melt a low-end phone 😛

lilactown00:11:28

I only recently joined this project. unfortunately, re-frame is fighting me on that front… our code is laid out in a way that makes it harder to split

superstructor04:11:24

If from this experience you learn there is anything you think that would help in future re-frame releases to improve code splitting please let me know and I'll take the feedback into consideration :thumbsup:

lilactown04:11:00

w.r.t., it adds to the problems because of the ability for any ns to fire off any event or subscribe to any subscription without noting the defining ns in it's requires

lilactown04:11:55

there's an implicit dependency in every ns on the fact that the event / fx / co-fx / subs have been registered already, that doesn't show up in it's dependency graph

p-himik07:11:28

Not sure if that will haunt me in the future, but somewhat recently I started requiring namespaces where the events/subs are defined and using fully-qualified keywords with namespace aliases. That way, there's an explicit dependency graph and you never dispatch an event or subscribe to a sub that has not yet been registered.

superstructor03:11:16

On my own projects, I also do fully qualified keywords as per @p-himik's comment above. I havn't yet used fully qualified keywords in re-frame for long enough (only ~2 years) to say definitively that long term its a good or bad plan but so far so good. It's not official re-frame guidance but it is an option that gives fully qualified dependencies on all event / fx / co-fx / subs. @U4YGF4NGM

lilactown00:11:44

yes luckily it is a SPA meant for enterprise desktop usage

41ph4_Ph4un06:11:31

Hey, dudes and dudettes!

41ph4_Ph4un07:11:43

I have an unusual question, does shadow-cljs provide anyway to have optimization/minification on for hotreloaded code when running watch? I know that's generally a no no but I have a bit peculiar use-case here.. I'm figuring out to a way to compile code that I use in Screeps, which is a Coding Sandbox game. The thing is that the code works wonderfully when I build a node library in release mode and use that in the game. Naturally the watched dev version includes some Shadow-CLJS-related stuff so that doesn't work. I'm well aware that what I'm asking might be impossible, but I figured if someone smarter/more experienced one would have an idea? Otherwise it's really nice to use shadow-cljs and it's not so bad having to manually build your code for this specific case 🙂

Eliraz08:11:14

can shadow-cljs work with lumo?

thheller09:11:43

@theamazingekko hot-reload is impossible with optimized code so no its not possible to have a release "watch"

👍 4
41ph4_Ph4un09:11:51

Thanks for the verification, assumed so 🙂

thheller09:11:48

if you run shadow-cljs server and then shadow-cljs release app whenever you need while that is running you at least won't pay for the startup cost each time

thheller09:11:08

or you can trigger the compile from the REPL too of course

41ph4_Ph4un09:11:54

yup, been taking the REPL-route so far and it's good. Just wondered about the concept :)

p-himik11:11:07

Just noticed that running release twice produces different files. Seems like Closure renames some objects in different ways. Is it possible to create the same output from the same input?

thheller11:11:23

did you delete the cache between the builds?

thheller11:11:47

so not running in CI or something?

p-himik11:11:05

Just two consecutive release commands on my local machine. Removed only the js and js.map files in between.

thheller11:11:29

did it recompile any CLJS?

thheller11:11:46

you can't really get the same output unfortunately

thheller11:11:00

as soon as any compilation happens that used any kind of gensym

p-himik11:11:49

> did it recompile any CLJS? Probably - it took some time and CPU. Ah yeah, gensym is in there somewhere. Although I would expect the order to be the same. But maybe that's not the case, or maybe the order doesn't matter.

thheller11:11:28

gensym is using an incrementing number thats reset whenever you restart the JVM

p-himik11:11:03

Yeah, but since I restart it each time I run release, the numbers should've been the same. Unless there's some concurrent work being done.

thheller11:11:04

so with caching the ids may change

thheller11:11:23

there is also parallel-build yes

p-himik11:11:33

Ahh, right. Damn.

thheller11:11:57

I wouldn't ever count on builds being identical

thheller11:11:10

shadow-cljs adds a couple closure compiler options to "try" that

thheller11:11:25

but that only really works if no CLJS compilation occurs .. which of course makes it pointless

p-himik11:11:49

Well, one could hope. 🙂 It's pretty often that I make only backend changes but CI runs the same pipeline either way and releases the UI bundle. But it's not a huge deal. And module splitting should make the situation a bit better, I assume. Whenever I finally start using it, that is.

thheller11:11:41

maybe separate the frontend/backend CI 😛

thheller11:11:41

you can check with shadow-cljs release app --verbose if any actual CLJS compilation occurs

thheller11:11:55

if not the builds should be the same. assuming of course that .shadow-cljs/builds is preserved between the builds (which may require additional CI config)

p-himik11:11:06

Ah, but that's more complicated and probably not worth my time. After all, I use macros and CLJC. Thanks, will try.

p-himik11:11:26

Yeah, a bunch of CLJS gets consistently recompiled on each run. So, I should look out for gensym and see if I can remove it. Is there anything else that I should look out for that could prevent a cached compilation result from being used?

p-himik11:11:58

Huh. Just checked gensym usages in CLJS files. It's definitely used. Only those files are never recompiled.

thheller11:11:51

you don't have to use it directly. any macro using gensym or auto# uses it for you

thheller11:11:04

maybe check why things get recompiled?

thheller11:11:16

I mean if they didn't change they should be cached. unless they have warnings or so.

p-himik11:11:26

I'm trying to check a particular file by commenting out suspicious imports and pieces of code and running the release twice.

p-himik12:11:43

Ah, prettifying cache files and comparing them is much faster. Yeah, it's definitely gensym. But only at some very particular places, and not the others. The same 6 files, each and every time. The offending calls to gensym are made by doto and by cljs-oops. But I use them in a much greater number of files. It's interesting how only those 6 get different results.

thheller12:11:29

why are they getting recompiled though?

p-himik12:11:24

Because gensym returns different result in different runs.

thheller12:11:33

I feel like you are digging into something thats kinda pointless. using when uses gensym

thheller12:11:57

any macro basically does

p-himik12:11:00

Yep. At this point, I'm trying to understand why those particular 6 files are affected. And others are not.

thheller12:11:19

so you are trying to figure out why they are getting recompiled?

p-himik12:11:39

Yep. Not so much to fix it but to understand what's going on.

thheller12:11:51

do you use clara-rules?

p-himik12:11:58

Never heard of it.

thheller12:11:02

do the files have warnings?

thheller12:11:12

do you explictely block the cache?

p-himik12:11:18

No warnings in those files. Only a couple of warnings from transit-js, and it's not used by those files. Nope, don't block anything.

thheller12:11:39

closure compiler warnings aren't relevant to this cache

thheller12:11:45

actual CLJS warnings only

p-himik12:11:18

No warnings at all then. Just lines like these

<- Cache read: hgs/platform/abc/core.cljs (8 ms)
-> Compile CLJS: hgs/platform/abc/core.cljs
[...]
<- Compile CLJS: hgs/platform/abc/core.cljs (282 ms)
[...]
<- Cache write: hgs/platform/abc/core.cljs (32 ms)
and the differences in variables' names in the cached JS in {:output {:js "..."}}.

thheller12:11:51

what the heck? is that from one compile?

thheller12:11:08

so you did make changes?

thheller12:11:15

because it read the cache but decided not to use it?

p-himik12:11:24

Let me double-check.

p-himik12:11:51

Yep, seems like it:

-> Cache read: hgs/platform/abc/core.cljs
<- Cache read: hgs/platform/abc/core.cljs (8 ms)
-> Compile CLJS: hgs/platform/abc/core.cljs
<- Compile CLJS: hgs/platform/abc/core.cljs (358 ms)
-> Cache write: hgs/platform/abc/core.cljs
<- Cache write: hgs/platform/abc/core.cljs (45 ms)

p-himik12:11:06

Two consecutive runs with grep abc/core, the same output.

thheller12:11:59

so something happened that made it not use the cache

p-himik12:11:06

Just in case it affects something - the file requires some JS files. Both from NPM and from a dir relative to src.

thheller12:11:35

ANY change may affect it yes

thheller12:11:52

anything used by the file that changes will invalidate the cache (as it should)

p-himik12:11:45

Oh, but the files didn't change!

p-himik12:11:52

Nothing was changed.

p-himik12:11:57

Except the system time.

thheller12:11:41

hmm then I don't know. you can compare the cache files before/after to see what changed besides the generated code

p-himik12:11:47

:compiled-at and some other timestamp-looking values (transit cached the keys, can't really tell without deserializing).

thheller12:11:51

you can use (shadow.build.cache/read-file (io/file ".shadow-cljs/builds/foo/....")) in the REPL with the full path to the cache file

p-himik13:11:28

*read-cache Ah, that was stupid. The JS files are not changed. But their timestamps are.

souenzzo13:11:32

[SOLVED]how do shadow-cljs watch files? hawk {:mvn/version "0.2.11"}

thheller14:11:35

@souenzzo thats only used on macos. otherwise the default JDK watcher is used.

😮 4
souenzzo14:11:21

which ns do that?

orestis14:11:31

Hawk is the library that does that.

thheller14:11:16

why do you ask?

souenzzo14:11:05

Because shadow-cljs watcher works extraordinarily well 🙂 I will study/try hawk for now https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/util/FileWatcher.java

thheller14:11:45

thats the hawk parts

thheller14:11:18

but again .. only used on macos

orestis16:11:45

I thought hawk wraps some native library to give the same api as the native JVM — is that not the case?

orestis16:11:56

> Like most clojure file watchers, hawk wraps the JDK 7 java.nio.file.WatchService API. This works great when Java has a native implementation for your platform of choice. However, this is not the case on OS X, so hawk also wraps the Barbary WatchService to provide performant monitoring even if you’re using a mac. An appropriate implementation is chosen automatically, so all you have to do is install hawk and relax.

thheller18:11:21

yes, using is on macos because of the barbary watchservice. hawk however does not provide the API I want so I had to wrap it.

thheller14:11:09

@p-himik yes timestamp changes are enough to invalidate cache

p-himik16:11:34

Fixed it, no cache misses now! And the file name stays the same, so that's great. But why is this step still executed though? Is it really needed given that the output file is already there and none of the cache entries got stale?

<- Closure - Optimizing ... (46580 ms)

p-himik11:11:30

@thheller Hi. Would it be possible to skip this step if nothing has changed? Or did something change and --verbose somehow didn't show it?

thheller11:11:03

there is no option currently but I could add a shadow-cljs release app --skip-optimize-if-unchanged or something

p-himik12:11:10

Hmm. Is there a reason to not make it the default behavior?

thheller12:11:54

yes, too much work to "verify" that it doesn't need to generate output

thheller12:11:32

ie. it can't possibly know that the output files exists when using :module-hash-names true

p-himik13:11:19

I think it could, if the manifest or cache contained all the relevant information, like hashes of all the source files that are fed into a particular version of GCC. But I have no idea how fragile that would be.

p-himik13:11:50

(ah, definitely not the manifest - just the cache then)

Pavel Klavík16:11:13

hi, is it possible to get source maps working with Web Workers? Debugging the following errors is difficult otherwise:

Uncaught Error: nth not supported on this type cljs.core/PersistentArrayMap
    at Function.eval [as cljs$core$IFn$_invoke$arity$3] (/js/compiled/cljs-runtime/cljs.core.js:6349)
    at eval (/js/compiled/cljs-runtime/orgpad.client.layout.webworker.ops.js:23)
    at Object.eval [as cljs$core$IFn$_invoke$arity$3] (/js/compiled/cljs-runtime/cljs.core.js:36362)
    at Function.G__27279__2 [as cljs$core$IFn$_invoke$arity$2] (/js/compiled/cljs-runtime/cljs.core.js:14986)
    at eval (/js/compiled/cljs-runtime/cljs.core.js:8044)
    at Function.eval [as cljs$core$IFn$_invoke$arity$3] (/js/compiled/cljs-runtime/cljs.core.js:8045)
    at Object.eval [as cljs$core$IReduce$_reduce$arity$3] (/js/compiled/cljs-runtime/cljs.core.js:10474)
    at Function.eval [as cljs$core$IFn$_invoke$arity$3] (/js/compiled/cljs-runtime/cljs.core.js:8198)
    at Object.orgpad$client$layout$webworker$ops$apply_ops [as apply_ops] (/js/compiled/cljs-runtime/orgpad.client.layout.webworker.ops.js:177)
    at eval (/js/compiled/cljs-runtime/orgpad.client.layout.webworker.handle.js:71)

awb9918:11:30

I am trying to make a bundle with shadow-cljs that uses quil library, that has as transient dependency p5

awb9918:11:08

cat package.json { "dependencies" : { "moment" : "^2.24.0", "p5" : "^0.10.2", "shadow-cljs" : "^2.0.41" } }

awb9918:11:26

cat shadow-cljs.edn {:dependencies [[org.pinkgorilla/gorilla-renderable "2.1.2"] [quil "3.1.0"] [awb99/shapes "0.1.2"] [awb99.fortune "0.0.1"]], :source-paths ["src"], :builds {:gorilla {:target :bootstrap, :output-dir "out/gorilla", :exclude #{cljs.js}, :entries [fortune.core pinkgorilla.ui.gorilla-renderable quil.sketch quil.middleware quil.core "moment"]}}}

awb9918:11:43

t\"; ignoring it"} {:line 98747, :column 13, :message "illegal use of unknown JSDoc tag \"submodule\"; ignoring it"} {:line 98748, :column 13, :message "illegal use of unknown JSDoc tag \"for\"; ignoring it"} {:line 99350, :column 13, :message "illegal use of unknown JSDoc tag \"submodule\"; ignoring it"} {:line 99351, :column 13, :message "illegal use of unknown JSDoc tag \"for\"; ignoring it"}], :resource-name "node_modules/p5/lib/p5.js", :js-requires [], :js-errors [{:line 69565, :column 0, :message "Invalid delete operand. Only properties can be deleted."}], :goog-requires [], :tag :shadow.build.npm/errors, :uses-global-buffer false, :uses-global-process false} ExceptionInfo: errors in file: /home/andreas/Documents/gorilla/kernel-cljs-shadowdeps/node_modules/p5/lib/p5.js shadow.build.npm/get-file-info* (npm.clj:549) shadow.build.npm/get-file-info* (npm.clj:472) shadow.build.npm/get-file-info (npm.clj:586) shadow.build.npm/get-file-info (npm.clj:583) shadow.build.npm/find-resource (npm.clj:677) shadow.build.npm/find-resource (npm.clj:638) shadow.build.resolve/find-npm-resource (resolve.clj:109) shadow.build.resolve/find-npm-resource (resolve.clj:80) shadow.build.resolve/fn--11285 (resolve.clj:235) shadow.build.resolve/fn--11285 (resolve.clj:211) clojure.lang.MultiFn.invoke (MultiFn.java:244) shadow.build.resolve/resolve-string-require (resolve.clj:349) shadow.build.resolve/resolve-string-require (resolve.clj:332) shadow.build.resolve/resolve-require (resolve.clj:545) shadow.build.resolve/resolve-require (resolve.clj:538) shadow.build.resolve/resolve-deps/fn--11240 (resolve.clj:51) clojure.lang.PersistentVector.reduce (PersistentVector.java:343) clojure.core/reduce (core.clj:6827) clojure.core/reduce (core.clj:6810) shadow.cljs.util/reduce-> (util.clj:47) shadow.cljs.util/reduce-> (util.clj:46) shadow.build.resolve/resolve-deps (resolve.clj:49) shadow.build.resolve/resolve-deps (resolve.clj:33) shadow.build.resolve/resolve-symbol-require (resolve.clj:532) shadow.build.resolve/resolve-symbol-require (resolve.clj:491) shadow.build.resolve/resolve-require (resolve.clj:542) shadow.build.resolve/resolve-require (resolve.clj:538) shadow.build.resolve/resolve-deps/fn--11240 (resolve.clj:51) clojure.lang.PersistentVector.reduce (PersistentVector.java:343) clojure.core/reduce (core.clj:6827) clojure.core/reduce (core.clj:6810) shadow.cljs.util/reduce-> (util.clj:47) shadow.cljs.util/reduce-> (util.clj:46) shadow.build.resolve/resolve-deps (resolve.clj:49) shadow.build.resolve/resolve-deps (resolve.clj:33) shadow.build.resolve/resolve-symbol-require (resolve.clj:532) shadow.build.resolve/resolve-symbol-require (resolve.clj:491) shadow.build.resolve/resolve-require (resolve.clj:542) shadow.build.resolve/resolve-require (resolve.clj:538) shadow.build.resolve/resolve-deps/fn--11240 (resolve.clj:51) clojure.lang.PersistentVector.reduce (PersistentVector.java:343) clojure.core/reduce (core.clj:6827) clojure.core/reduce (core.clj:6810) shadow.cljs.util/reduce-> (util.clj:47) shadow.cljs.util/reduce-> (util.clj:46) shadow.build.resolve/resolve-deps (resolve.clj:49) shadow.build.resolve/resolve-deps (resolve.clj:33) shadow.build.resolve/resolve-symbol-require (resolve.clj:532) shadow.build.resolve/resolve-symbol-require (resolve.clj:491) shadow.build.resolve/resolve-require (resolve.clj:542) shadow.build.resolve/resolve-require (resolve.clj:538) shadow.build.resolve/resolve-deps/fn--11240 (resolve.clj:51) clojure.lang.PersistentVector.reduce (PersistentVector.java:343) clojure.core/reduce (core.clj:6827) clojure.core/reduce (core.clj:6810) shadow.cljs.util/reduce-> (util.clj:47) shadow.cljs.util/reduce-> (util.clj:46) shadow.build.resolve/resolve-deps (resolve.clj:49) shadow.build.resolve/resolve-deps (resolve.clj:33) shadow.build.resolve/resolve-symbol-require (resolve.clj:532) shadow.build.resolve/resolve-symbol-require (resolve.clj:491) shadow.build.resolve/resolve-require (resolve.clj:542) shadow.build.resolve/resolve-require (resolve.clj:538) shadow.build.resolve/resolve-entry (resolve.clj:552) shadow.build.resolve/resolve-entry (resolve.clj:551) clojure.lang.PersistentVector.reduce (PersistentVector.java:343) clojure.core/reduce (core.clj:6827) clojure.core/reduce (core.clj:6810) shadow.cljs.util/reduce-> (util.clj:47) shadow.cljs.util/reduce-> (util.clj:46) shadow.build.resolve/resolve-entries (resolve.clj:563) shadow.build.resolve/resolve-entries (resolve.clj:554) shadow.build.targets.bootstrap/resolve (bootstrap.clj:56) shadow.build.targets.bootstrap/resolve (bootstrap.clj:48) shadow.build.targets.bootstrap/process (bootstrap.clj:278) shadow.build.targets.bootstrap/process (bootstrap.clj:271) clojure.lang.Var.invoke (Var.java:384) shadow.build/process-stage/fn--12425 (build.clj:148) shadow.build/process-stage (build.clj:145) shadow.build/process-stage (build.clj:137) error Command failed with exit code 1.

awb9918:11:29

I am getting crazy errors, does anybody know why they come?

thheller18:11:46

what kind of errors?

awb9918:11:06

I copied my configs nd the error ito the therad to keep the main chat clean

thheller18:11:21

hmm looks like p5.js does something weird ... check what happens in the :line it lists

thheller18:11:46

:js-errors [{:line 69565, :column 0, :message "Invalid delete operand. Only properties can be deleted."}]

awb9918:11:28

the error log is huge

awb9918:11:40

I am trying to redirect it to a pastebin

awb9918:11:10

but seems like I cannot redirect the error message itself

awb9918:11:56

this link contains most errors

thheller18:11:32

I already pasted you the relevant one

thheller18:11:49

its a bunch of warnings .. which you can ignore. the :js-errors above is the one causing the crash

awb9918:11:17

Thanks Thomas!

awb9918:11:54

Can I see somewhere the version of the npm modules for which you have provided cljsjs wrappers?

thheller18:11:13

hmm? they don't have a "version"?

thheller18:11:25

I mean the wrappers don't have a version

awb9918:11:27

Well, in the wrappers I dont see them at least.

thheller18:11:33

you decide the version of the npm package?

awb9918:11:07

But what if there is a breaking change in the library?

awb9918:11:38

nevermind

awb9918:11:58

your wrappers seem to all just set one symbol in the window

awb9918:11:02

that wont break

thheller18:11:46

ideally you wouldn't use those wrappers at all

awb9918:11:05

So what happened to me is that the clojurescript compiler detected something bad in the npm bundle of p5 ?

awb9918:11:09

the "delete this"

awb9918:11:47

That is an amazing benefit of shadow-cljs!

thheller18:11:50

well not really .. the code probably runs otherwise. if they'd run their code through a linter that would likely catch it too

awb9918:11:29

but that one function would break

awb9918:11:36

or throw exceptions

thheller19:11:37

yeah .. but people probably use p5 without it ever breaking

thheller19:11:13

you can probably report that error ... maybe they care about fixing it

awb9919:11:45

Good point!

thheller20:11:54

wow. nice response time on that issue 🙂

aiba20:11:21

I'm using shadow with :target :react-native (based on the reagent-react-native template) and connecting to the cljs repl from emacs/cider. One unexpected behavior is that when I evaluate an expression over cider that throws an error, the trace is displayed in the simulator window, and no feedback is given in emacs/cider. Is that expected? Is there a way to get the exception to come back to emacs/cider so I can see it there?

thheller20:11:35

no clue. I don't do react-native development myself.

thheller20:11:51

the eval happens in a try/catch so the react-native side probably intercepts the error somehow?

aiba20:11:19

OK, so the eval is expected to wrap a try/catch and return the error to cider then

aiba20:11:50

So yeah, seems like something about the react-native environment is intercepting the exception. Any tips on how to track that down?

thheller20:11:33

I know next to nothing about react-native sorry 😛

aiba21:11:19

Yeah no worries, I just tried it with a regular :target :browser shadow project, and a similar thing happens, it just prints the error in the repl buffer instead of popping up a *cider-error* buffer. So I'm guessing that this is actually something to do with how shadow emulates piggieback.

thheller21:11:56

thats possible ... I don't use emacs so no clue what *cider-error* even is

aiba21:11:19

Don't mean to belabor this, but if you're curious, there must be something in the piggieback repl protocol that tells cider that an error occured during evaluation, which cider uses to present some UI indicating this. It appears that shadow emulates piggieback in a way that cider doesn't get this info. If there's any interest in tracking this down, I'd be thrilled to help, but if you consider it too low priority, that'd be understandable.

thheller21:11:51

happy to add whatever is needed but I have no energy to spend on tracking yet another cider nrepl issue myself

thheller21:11:13

since there are no docs for any of this it is likely just an nrepl message missing some field or something

Pavel Klavík21:11:56

Hi, is it possible to get REPL for a Web Worker?

Pavel Klavík21:11:59

and concerning the source maps I mentioned above? I am just able to get to Javascript code which is not very useful to debug Clojurescript

Pavel Klavík21:11:39

source maps in Web Worker

thheller21:11:26

I'm not sure why they don't work. I'd expect them to work. haven't used workers myself for years

thheller21:11:11

if you open an issue I can take a look when I have some time

thheller21:11:17

deep in other stuff currently

Pavel Klavík21:11:27

sure, I will produce some example and make an issue, thx

Pavel Klavík21:11:13

outside of these two issues, I am quite happy with Web Workers support in Shadow, super easy to get it working

kanwei22:11:16

is there any way to include CSS?

import DatePicker from "react-datepicker";
 
import "react-datepicker/dist/react-datepicker.css";

lilactown22:11:40

not in your code. you can copy the datepicker .css file into the folder your js is hosted in and add it to your index.html