Fork me on GitHub
#shadow-cljs
<
2023-03-22
>
p-himik11:03:53

Is there a way to do this in shadow-cljs? https://docs.sentry.io/platforms/javascript/configuration/tree-shaking/#tree-shaking-optional-code-with-webpack Seems like somehow setting a global __SENTRY_DEBUG__ to false and letting GCC know about it should achieve the same?

thheller13:03:42

why are they shipping debug code by default?

p-himik13:03:37

What would be an alternative? IIRC React also does it, only it checks for the value of process.env.NODE_ENV and not some custom constant.

p-himik13:03:31

Also, given that there's no comparable setting, would it make sense to implement it? Not sure how feasible it is with GCC.

thheller13:03:53

do you have an example of where this is actually used?

thheller13:03:34

I know what DefinePlugin does, but I need to see the actual code shadow-cljs is processing to comment

p-himik13:03:22

Sure, here's a piece of code from Sentry:

function flush(timeout) {
  const client = core.getCurrentHub().getClient();
  if (client) {
    return client.flush(timeout);
  }
  (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.warn('Cannot flush events. No client defined.');
  return utils.resolvedSyncPromise(false);
}

thheller13:03:25

do you have the file that is in please?

thheller13:03:43

http://unpkg.com or something, not the source file. the actual published file in the npm package

p-himik13:03:41

Yeah, that was from the package. :)

thheller13:03:51

what should work is just search&replace (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) with false before shadow-cljs processes it

p-himik13:03:50

Sounds like it would be incredibly brittle, no?

p-himik13:03:22

Just as a guess, tried (js* "/** define {boolean} */\nvar __SENTRY_DEBUG__ = false;") in the first loaded ns but didn't work.

thheller13:03:30

thats the only solution I can offer šŸ˜›

thheller13:03:52

yeah, this isn't a closure define. so it won't be recognized as such

thheller13:03:09

well, doing it on the CLJS parts won't do anything since they aren't compiled together

p-himik13:03:29

So GCC doesn't use the values of what it considers constants? Only goog.defines?

thheller13:03:43

thats not the main problem there

thheller13:03:00

the first problem that would need solving is making it part of the code that is actually getting compiled

thheller13:03:15

there currently is no :prepend-js option for npm code

thheller13:03:00

appending __SENTRY_DEBUG__ = false to the files in the npm package might work

thheller13:03:12

less brittle than search and replace, still manual though

thheller13:03:38

can't offer you anything built-in from shadow-cljs because there is nothing

p-himik13:03:40

Ah, so the problem is that my project's code is basically appended to the NPM code that's already been processed? Those two aren't processed together?

p-himik13:03:01

What if it's not an NPM module but a plain JS file in my project's sources?

thheller13:03:48

that is not compiled together with npm sources

thheller13:03:05

the problem is this is commonjs

thheller13:03:18

so you can't just set a var in one file and expect that to be available in all files

thheller13:03:46

and I'm not sure the closure compiler will recognize global.__SENTRY_DEBUG__ = false;

thheller13:03:19

thats why I'm saying to append it to every file

thheller13:03:33

that should work

p-himik13:03:12

Ah, every file. Gotcha. And --define='__SENTRY_DEBUG__=false' also won't work, right?

thheller13:03:43

don't know what --define is in this context? it is not a shadow-cljs option

p-himik13:03:11

Yeah, it's for GCC.

p-himik13:03:26

Dunno if it's exposed in its SDK.

thheller13:03:44

no clue either. never used the CLI code from GCC

thheller13:03:57

I'd assume this is just setting a closure-define, in which case the answer is no

thheller13:03:23

I'm not aware of a setting in the closure compiler that would be equiv to the webpack DefinePlugin

thheller13:03:28

if you find something let me know

p-himik13:03:43

It redefines a global constant at compile-time. But if I understand the docs correctly, it would only work if there is a constant with that name.

thheller13:03:24

they also likely define "global constant" difrently than commonjs

p-himik14:03:31

Struggling to find a way to make the CLI work. Maybe you have a clue? Running

google-closure-compiler --module_resolution NODE \
 --process_common_js_modules \
 --js ./test.js \
 --js 'node_modules/@sentry/**/cjs/**.js' \
 --js 'node_modules/@sentry/**.json'
where test.js is just
/** define {boolean} */
var __SENTRY_DEBUG__ = false;
but it results in
node_modules/@sentry/browser/cjs/client.js:3:13: ERROR - [JSC_JS_MODULE_LOAD_WARNING] Failed to load module "@sentry/core"
  3| const core = require('@sentry/core');
                  ^
[...]
Not sure how to tell it that @sentry/core is one of those files.

thheller14:03:13

echo "var __SENTRY_DEBUG__ = false;" > node_modules/@sentry/browser/hack.js

thheller14:03:28

(:require ["@sentry/browser/hack.js"])

thheller14:03:40

would compile it together with everything else

thheller14:03:47

but I can already say this won't do anything

p-himik14:03:51

Nah, I would like to experiment a bit with the CLI first. :) Maybe there's a way to do it without hacks.

thheller14:03:17

sure, have fun. can't help, never used it beyond compiling a single file without any requires

šŸ‘ 2
p-himik14:03:04

And while I'm busy wasting my time with that - Sentry also distributes ESM modules. Would that make any difference w.r.t. the original problem?

p-himik16:03:42

I think I was able to successfully inline the __SENTRY_DEBUG__=false and subsequently eliminate all the blocks that used it. However, I had to use advanced optimizations and IIRC node modules only go through simple optimizations, which does not include variable inlining.

thheller16:03:30

simple does eliminate some stuff

thheller16:03:30

node.getQualifiedName().equals("process.env.NODE_ENV");

p-himik16:03:31

How does GCC know about process.env.NODE_ENV?

thheller16:03:58

I'm replacing it as an optimization pass

thheller16:03:02

so that it becomes if ('production' === 'production') { and simple does eliminate that

p-himik16:03:07

Ahh, so it's shadow-cljs that enables that particular inlining. :) Perhaps that inlining step could be made configurable? So people would be able to replicate DefinePlugin by inlining whatever they want.

thheller17:03:18

not sure what kind of performance impact this would have. generally not a fan of replicating every webpack feature. how much does this actually eliminate?

p-himik17:03:39

Not sure how to compare that properly. I can easily run one of the following: ā€¢ Advanced + inlining of false with a subsequent elimination of the debugging lines ā€¢ Advanced + inlining of true, so without elimination of those lines ā€¢ Simple without inlining To properly compare with what a potential change to shadow-cljs I'd need to be able to run "simple with inlining and elimination". > not a fan of replicating every webpack feature Doesn't this feature sound like something that's useful in general, regardless of what webpack can do?

thheller17:03:53

I gave you a couple suggestions on how you could potentially get the inlining. try them and report the results. then I might consider it. in the dark no, I don't think this is useful. JS libs are riddled with these non-standard webpack specific things

thheller17:03:51

should be a 5 line script to append const __SENTRY_DEBUG__ = false; to every file

p-himik17:03:52

The first 2 options, when compared, have a difference of 3% in size: 32693 vs 33605. But it's just this particular case, for the particular files that I checked - not the whole of Sentry, far from it. > try them and report the results Sure, what's up?

thheller17:03:11

is that size diff gzip'd?

thheller17:03:40

why are you wasting your time on less than 1kb?

p-himik18:03:04

12405 vs 12738. I'm wasting my time on the concept, not on the size. :) In the process, I'm learning more about GCC and how different things work. It would be more interesting to compare the sizes of the whole of Sentry. Not sure how hard that would be.

p-himik18:03:26

Well, definitely harder than adding a bunch of files to the CLI arguments - started getting Inline JSDoc on default parameters must be marked as optional errors, no clue how to proceed, but probably not going to.

thheller18:03:16

cd node_modules/@sentry/browser; find . -name "*.js" -type f -exec sh -c 'echo "\nconst __SENTRY_DEBUG__=false;" >> "$0"' {} \;

thheller18:03:31

seems to eliminate the debug stuff

thheller18:03:55

about 1kb gzip in total

thheller18:03:26

do all the sentry packages do this or just the browser thing?

p-himik18:03:17

With this:

(ns hgs.x
  (:require ["@sentry/react"]
            ["@sentry/integrations"]
            ["@sentry/replay"]
            ["@sentry/tracing"]))

(defn main []
  (js/console.log 0))
and a similar setup I get gzipped sizes of 106388 and 111964. So ~5%.

p-himik18:03:35

Note that there's not only @sentry but also @sentry-internal.

thheller18:03:06

why would you bloat your build this massively with this mess šŸ˜›

p-himik18:03:24

Because it's worth it. Have you seen what it actually does?

p-himik18:03:20

Mere 100 kB for me to be able to never ask a user to record a video - sign me up! And that's just one of the features.

p-himik18:03:23

Heh, according to a shadow-cljs report my own sources + CLJS in that particular project go well beyond 2MB ungzipped. So yeah, I'm not too concerned about 100 kB at this point. :) Let alone 5% of 100 kB. I'm simply curious whether it's a change that would be worthwhile to have at the tool level, for everyone.

thheller18:03:01

so far nobody has ever asked for this, so can't imagine this being too common

p-himik18:03:34

Fair! Hmm, was sure I saw something similar in Vega and MUI, but apparently not - can't find it in the docs. In any case, seems like there's a clear way forward if that feature becomes needed. I'd like to work on it myself although of course can't provide any time estimates at the moment.

thheller09:03:44

FWIW I thought about this a bit. its definitely not just a simple replacement. since (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) would become (typeof false === 'undefined' || false) which doesn't do what you'd want šŸ˜›

p-himik09:03:11

Ah, right. But how does it work with React then, with process.env.NODE_ENV?

thheller13:03:59

but no, there is no comparable setting

dehli13:03:47

šŸ‘‹ I'm running into a bug with :esm and a side-effecting library. My shadow config looks like:

{:target :esm
 :js-options {:js-provider :import}}
and in my cljs, I have
(:require ["quill-mention"])
(the package executes a side-effect) If I do (:require ["quill-mention" :as foo]) and add a (js/console.log foo) my code works, but when I remove the log it the library isn't included. Note, this change only started occurring after 2.20.12 . Would it be helpful to create a minimal reproducible example or any quick ideas as to what the issue might be?

thheller13:03:53

hmm :import means shadow-cljs just emits import * as X from "quill-mention"; for that require?

thheller13:03:30

did you check if that is in the output shadow-cljs produces?

dehli13:03:58

ahhh, let me check. just realized i was looking at webpack generated code so this might be an issue with the webpack bundling instead of shadow

dehli13:03:40

Looks like if I grep for quill-mention in the shadow generated code it's not present (when I require it as (:require ["quill-mention"]). However, when I use (:require ["quill-mention" :as foo]) and console log it ( foo), then quill-mention is included in the bundle as expected.

dehli13:03:38

If you'd like, I can create an issue for it and then look into it in a bit Edit: created https://github.com/thheller/shadow-cljs/issues/1102

thheller14:03:09

I just checked. it is indeed eliminated.

thheller14:03:54

but yes. please open an issue. can't fix right now

šŸ‘ 2
pmooser14:03:35

I'm weirdly having some issues with hot reload at the moment. I've been using this successfully for years, but I'm not sure what changed. I have some functions marked with :dev/after-load and they aren't being called upon reload. Any idea how I might begin to debug this ?

pmooser14:03:02

There are a couple of build warnings, but not from shadow-cljs itself, so I don't imagine that should matter.

pmooser14:03:18

Oh, I see :ignore-warnings ... maybe that IS it ?

pmooser14:03:34

Yeah, that was it. Ok.

thheller14:03:16

from what else would you get warnings? šŸ˜›

pmooser14:03:30

I wouldn't expect some warning from a function that a library deprecated would just make reloading stop working, with no logging or anything. It's a legitimate decision, but I think it's funny to act like no one would be surprised by this.

thheller14:03:45

> but not from shadow-cljs itself

thheller14:03:14

I wondered what else you expected the warning to be from?

thheller14:03:39

or I misunderstood

thheller14:03:07

> I think it's funny to act like no one would be surprised by this.

pmooser14:03:13

Maybe I didn't explain well - I know what the warnings were from - just not always how to resolve them. So in this case, I was ignoring a deprecated function warning because I don't know how NOT to use that function at the moment (reagent.dom/dom-node) ... I was just surprised when that made shadow-cljs not reload things any more.

pmooser14:03:19

I'm not surprised by where the warnings came from,

thheller14:03:24

don't know where that came from. I'm not surprised that you didn't know this setting, why would you

pmooser14:03:35

I was surprised that the presence of warnings caused shadow-cljs to not reload.

thheller14:03:16

it doesn't reload because it may break stuff. some warnings will actually lead to runtime errors when trying to run it anyways

thheller14:03:24

thats why its the default to now reload with warnings

pmooser14:03:44

Yeah ... and I'm reluctant to ignore these warnings or turn them off for that very reason.

pmooser14:03:03

For now I'll just use :ignore-warnings and then I will turn it back off again.

pmooser14:03:07

Sorry for the noise and distraction!

Ben Lieberman15:03:43

Hey, I'm interested in adding some Reagent components to an existing PHP codebase. I got it working yesterday by outputting the compiled JS into the assets directory served by Apache, but that's outside the cljs project root. It breaks the ws connection for watching the build. Is there a way to keep the watch running but output code outside the project, or should I just create a project in the PHP assets dir?

thheller15:03:39

I do not understand that description. what does a WS connection have to do with where the output lives?

thheller15:03:17

shadow-cljs doesn't care where the output is? or what serves it for that matter

Ben Lieberman15:03:51

I'm not sure myself. I just know that when I open the page in the browser the console warns me that shadow cljs can't connect.

thheller15:03:36

and what does it say exactly?

thheller15:03:57

is the apache just running on the different host? domain?

Ben Lieberman15:03:16

No same domain, but actually ignore me, it works now. Thanks.

Ben Lieberman15:03:28

Idk what changed between yesterday and today.