Fork me on GitHub
#shadow-cljs
<
2021-08-25
>
lilactown14:08:18

that looks pretty much exactly what i'm running into

lilactown14:08:40

I agree that I was surprised my file changes were causing a hot reload after requiring it from a REPL

Jimmy Miller19:08:38

So I am in the unfortunate situation of having to include some pre-minified javascript into my project. I am using the node-library target and trying to export something that another javascript library can consume.

{:target :node-library
 :output-to "out/lib.js"
 :compiler-options {:infer-externs :auto}
 :exports {:hello react-cljs-poc.core/app}
I can run everything in dev fine, but when I go to do this build, closure is taking some of that already minified code and changing some keys in some objects. Those keys can't change sadly. The keys look something like this:
var Me = {
  day: {},
  week: {},
  month: {},
  year: {}
}
And after the closure compiler is done doing its magic it looks like this:
var Ij = {
  day: {},
  pd: {},
  month: {},
  year: {}
}
Short of turning off advanced compilation, is there anything I can do to make closure not do these sorts of transforms?

Jimmy Miller19:08:56

I'm guessing there is some manual externing I could try to do prevent this. But anytime I get this new minified code I'd have to redo that, which feels suboptimal

thheller19:08:10

how do you create the object?

thheller19:08:35

if you create it via (js-obj "day" 123 "week" 123) it should be safe from renaming

thheller19:08:55

:compiler-options {:infer-externs :auto} is the default, you don't need to configure that

👍 3
thheller19:08:37

basically the answer to your questions depends on what you do in the code

thheller19:08:59

hmm nevermind about js-obj, misremembered that

Jimmy Miller19:08:19

No, the object is in the pre-existing minified js

Jimmy Miller19:08:10

I don't directly touch that object at all. The js exports a react component. I call that react component. Right now that is literally it. No props, no nothing.

lilactown20:08:58

how is the minified JS included in the project?

Jimmy Miller23:08:07

I had just straight it as a file and required it. I’m beginning to feel like I should have put it in node_modules. Will work on it again tomorrow.

thheller05:08:48

what does "No, the object is in the pre-existing minified js" mean? how is it included in your build?

thheller05:08:54

if you include it via https://shadow-cljs.github.io/docs/UsersGuide.html#classpath-js then it will end up going through advanced. so the only way to protect it there would be externs

Jimmy Miller13:08:26

Yeah, I was just straight up requiring a js file in the classpath. I moved it into node_modules and it worked. Figured that out after reading this > Resolves the JS via node_modules and includes a minified version of each referenced file in the build. It is the default for the :browser target. node_modules sources do not go through :advanced compilation. For me, I'd love to just be able to toggle that on a file level rather than having to put files in some particular location to get that behavior. But I get it and it is probably best that I go take this code and make it a package. (It is some propriety code that as shipped as a zip of poorly minified js. What I mean by the object being in the pre-existing minified js is that it is just an object literal in that js source file. I didn't realize requiring js files from node_modules vs requiring js files from elsewhere would have a behavior difference. I do admit I find that a bit strange.)

wombawomba19:08:09

So I'd like to get a basic in-browser REPL going. Is this something I could leverage shadow-cljs for?

isak20:08:28

Yea kind of. If you start the build, then go to the shadow-cljs dashboard (localhost:9630 for me), then you can go to the Inspect Latest tab and use the "Runtime Eval" box at the bottom.

wombawomba21:08:41

Hmm.. all I see there is No Taps yet. (tap> something) to see it here. Guess I'll try updating shadow-cljs and see if that helps (I'm on 2.12.4).

thheller05:08:42

what does "get a basic in-browser REPL going" mean? shadow-cljs browser-repl does that?

thheller05:08:52

or do you mean embedded in something?

thheller05:08:44

ah .. in browser you mean. so from within your app?

thheller05:08:54

if its in development you can build on top of cljs-eval see https://clojureverse.org/t/status-update-inspect-cljs-eval/6074

thheller05:08:48

only works while the watch is running though. but besides that you can build all UI you want yourself

wombawomba09:08:14

yeah exactly, in my app itself

wombawomba09:08:55

I'll try building something on top of cljs-eval, thanks

thheller09:08:22

just to be clear: this is for local development only. this can't be part of a "release" app as shadow-cljs should never run in any production environment

thheller09:08:55

but if you just want something you can add via :preloads to aid local development that is totally fine

wombawomba09:08:59

...on a related note, would it be possible to provide self-hosted CLJS (for eval) as a separate module, that can be loaded dynamically?

wombawomba09:08:16

self-hosted is fine for now, but it sure would be useful to have something like this in production as well

thheller09:08:46

no, not possible per se. you could run it in an iframe or so separately but not part of the main app running in the same context

wombawomba09:08:17

because of name mangling?

thheller09:08:33

yeah, if you want to run everything in :simple then it is possible

wombawomba09:08:29

in theory, would it be possible to "co-mangle" the names for self-hosted/non-self-hosted code so they'd be compatible?

thheller09:08:23

no. it is not just about mangling. :advanced does so much more. removing dead code, inlining, collapsing namespaces, etc

thheller09:08:46

basically namespaces don't exist anymore with no way to restore them or map them back to anything meaningful

wombawomba09:08:23

...okay so how about this then

wombawomba09:08:40

could I provide both simple and advanced builds?

wombawomba09:08:34

so I'd default to advanced (for users) but somehow enable developers to run a (equivalent) self-hosted simple build

thheller09:08:10

sure. don't know what problem that would solve though?

wombawomba09:08:16

debugging in production

wombawomba09:08:04

I use re-frame so if something is broken then I could just dump all my state and open it in the self-hosted build

thheller09:08:23

I have had "give me all the data" type of debug hooks in my apps

thheller09:08:57

so (defn ^:export debug-me [] (pr-str @re-frame.db/the-thing)) or so

wombawomba09:08:05

yeah exactly

thheller09:08:13

then just import the data in a REPL locally and look at it

wombawomba09:08:13

although I'm thinking I could use local storage to copy the state over to a new tab/window (that'd open something like .domain/$current_path) to make the whole process completely transparent

wombawomba09:08:57

(debug.my.domain would serve the self-hosted build to IPs in my VPN)

thheller09:08:58

yeah but in my experience the problem isn't the data. the problem is "how did it get into this shape"

thheller09:08:51

but having some way to get the data is always useful

wombawomba09:08:58

I'm already tracking and displaying re-frame state changes though so that's already solved

wombawomba09:08:24

I'm embedding something similar to re-frame-10x in my app, so devs can see what's happening

wombawomba09:08:52

for some situations though, I find it really useful to be able to manipulate data

wombawomba09:08:03

so that's the main argument for having a REPL

wombawomba09:08:25

(I mean, manipulate the data and have the changes reflected in the app)

thheller09:08:54

sure, but just as the export you can also have a import thingy that just sets the new state

thheller09:08:45

going full self-hosted means you are changing so much more about the structure of the code that you might not even run into the issues you are trying to debug in the first place

thheller09:08:51

eg. if they are externs related

thheller09:08:20

and if you are able to replicate the problem after switching to self-hosted mode you should also be able to replicated in a local watch?

wombawomba09:08:16

yeah, but if I do it "in production" then it's trivial to reuse my session etc

wombawomba09:08:39

I think replicating in a local watch would involve a lot of effort to work around stuff like my production CSP policy

wombawomba09:08:14

still, if I had "dump/import data" buttons then I could support both those use-cases

wombawomba09:08:37

for some problems access to the production backend probably isn't that important

thheller09:08:58

yeah no clue what your app looks like. I have done the export thingy in the past and it helped debugging for sure but much less than first expected 😉

thheller09:08:35

I was super excited at first and thought this is going to solve everything but ended up just being "hmm ok data is messed up but why?"

thheller09:08:47

the data being corrupted I could already tell from the UI not rendering properly 😛

thheller09:08:26

but I didn't have a track-all-changes thing so that would have helped a lot too I guess

thheller09:08:46

just looking at a snapshot alone definitely is not all that useful

wombawomba10:08:28

yeah I'm gonna go ahead and build this, and we'll see how useful it turns out to be 🙂

👍 3
thheller10:08:18

let me know how it turns out. I'm always interested in such things.