Fork me on GitHub
#shadow-cljs
<
2023-04-06
>
thheller05:04:56

doesn't do anything useful in cljs anyways

seanstrom14:04:03

Hey @thheller 👋 Can you help me with this error that I’m coming across? • I’ve been using shadow-cljs version 2.20.18 for a while and I tried upgrading to the latest version. • Unfortunately, my project started to display warnings from shadow-cljs during hot reloads: ◦ Failed to load app/main.cljs TypeError: goog.provide is not a function ◦ This error seem to originate at version 2.22.0 • My build settings are configured around using the npm-module output alongside a JS bundle tool (parcel). ◦ Here’s my minimal reproduction of the error: https://github.com/seanstrom/cljs-dev-template/tree/debug-build • I would like to debug this more, but I’m not sure how to setup shadow-cljs for dev. ◦ Any instructions or tips for debugging each commit of shadow-cljs? Let me know if you need any more information, would really appreciate the help since I’m a bit stuck.

thheller14:04:03

it is unclear what you are trying to achieve with this template?

thheller14:04:07

what is parcel doing there?

thheller14:04:22

I'm almost 100% certain it has something to do with parcel

thheller14:04:20

the problem appears to be that there is a different goog in scope, one that isn't properly initialized. not sure why though.

seanstrom15:04:46

Based on the exception it seems to be saying that when my app.main.js file is reloaded it evaluates a goog.provide expression and doesn’t have that defined. Parcel is being used to handle the bundling of the npm-module, it should be only importing the assets declared inside of here: https://github.com/seanstrom/cljs-dev-template/blob/debug-build/src/cljs-index.js From what I can see, after I save a change to main.cljs , a script is evaluated for the changes by the shadow devtools. In version 2.21.0 the script will evaluate a script shaped like this:

"var COMPILED = false;

var cognitect=$CLJS.cognitect || ($CLJS.cognitect = {});
var clojure=$CLJS.clojure || ($CLJS.clojure = {});
var cljs=$CLJS.cljs || ($CLJS.cljs = {});
var shadow=$CLJS.shadow || ($CLJS.shadow = {});
var goog=$CLJS.goog || ($CLJS.goog = {});
var app=$ || ($ = {});
var com=$ || ($ = {});

$CLJS.SHADOW_ENV.setLoaded("app.main.js");

goog.provide('app.main');
app.main.render = (function app$main$render(){
var change_me_21185 = "Hello";
var node_21186 = document.getElementById("root");
var text_node_21187 = document.createTextNode(change_me_21185);
node_21186.appendChild(text_node_21187);

return cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2(["[main]: render"], 0));
});
In version 2.22.0 it evaluates a script shaped like this:
"goog.provide('app.main');
app.main.render = (function app$main$render(){
var change_me_21191 = "Good Bye";
var node_21192 = document.getElementById("root");
var text_node_21193 = document.createTextNode(change_me_21191);
node_21192.appendChild(text_node_21193);

return cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2(["[main]: render"], 0));
});
It seems something related to how the devtools code reload npm-modules has changed between these two versions. I noticed in the change logs that there could be some related changes in the release of version 2.22.0 Any ideas what could be happening here?

thheller15:04:11

don't do any hot reload at all

thheller15:04:18

just open the webpage

thheller15:04:23

in the console type goog

thheller15:04:27

and then $CLJS.goog

thheller15:04:37

they should be the same object but for some reason are not

thheller15:04:40

I don't know why

thheller15:04:05

but that is the source of the problem. hot-reload will eval in the global scope, thus use the goog object. which for some reason only exists partially

thheller15:04:15

var goog = $CLJS.goog = {}; this line should make sure that it is always the same object

thheller15:04:25

I don't get why it isn't

seanstrom15:04:30

Well during startup that’s present, but at reload it’s missing, so goog becomes the window.goog and not the $CLJS.goog right?

thheller15:04:19

var global = arguments[3];

thheller15:04:32

this might be the problem. this isn't something shadow-cljs injects. might be parcel doing that?

thheller15:04:50

maybe it has its own notion of what global means?

thheller15:04:13

oh just noticed that your build config is missing :runtime :browser

thheller15:04:42

hmm but that doesn't seem to change anything

thheller15:04:55

I hate :target :npm-module. is there a specific reason you are using that?

thheller15:04:14

and not :target :esm?

thheller15:04:57

or even :js-provider :external?

seanstrom15:04:19

I added the :runtime :browser , but it didn’t fix the problem. I started using the npm-module format because I couldn’t use the esm format for similar hot-reloading issues. In the esm target, the hot reloading attempts to redefine an existing namespace and throws an exception. Until recently I didn’t have any issues with npm-module format, but I would prefer esm as an option too. I’m not sure what :js-provider :external will do here, but I can try.

seanstrom15:04:25

In general it seems that maybe the code was relying on that variable statement to always be within the hot reloaded script. I assume it was removed to keep things small, so maybe I can override goog manually and see if my problems are fixed temporarily.

thheller15:04:05

if (process.env.NODE_ENV !== "production") {
  require("../cljs-dist/shadow.cljs.devtools.client.browser.js")

  window.goog = $CLJS.goog;
}

thheller15:04:09

that seems to do it

thheller15:04:15

don't ask the me why that is necessary though 😛

seanstrom15:04:53

When the script is evaluated it seems to use the window.goog because window is the default global context right? Maybe most of the code just uses the $CLJS namespace most of the time?

thheller15:04:15

hmm I can't remember. :npm-module is one hack after another and not exactly ideal 😛

thheller15:04:41

I would recommend using :esm if you can

seanstrom16:04:32

Well if window.goog is suppose to be $CLJS.goog could that be defined in cljs_env.js? I’ll try esm and I can show you the other output that I get, one sec.

thheller16:04:54

but I remember something now. I might actually have broken this not too long ago unintentionally

seanstrom16:04:45

Ah okay, we do you think it might have happened?

thheller16:04:45

so 2.22.0 likely is the first breaking version

thheller16:04:01

not exactly sure what yet though, still just guessing

seanstrom18:04:58

I’m trying to debug each commit inside the 2.22.0 release. How do I build the project with all pieces pinned to a specific commit so I can run each commit as a release?

thheller19:04:54

no need. it is the commit I linked

seanstrom19:04:54

Well how would test that previous commit still works?

thheller19:04:04

just test 2.19?

seanstrom19:04:01

If I make a changes to the source code I do I test the changes with a build. I need the latest changes so I can’t use the old build otherwise it just works

thheller19:04:05

then just checkout the version you want

thheller19:04:14

I always just run lein install for local testing

thheller19:04:15

you can build some of the supplemental stuff too if you intend to use the UI or so

thheller19:04:20

but its not required

seanstrom19:04:26

Okay I’ll try that, I’ve been running build-all until now.

thheller19:04:35

that also works

thheller19:04:01

you can also just add shadow-cljs as a usual dependency

thheller19:04:25

and then add :source-paths ["src/main" "../shadow-cljs/src/main" "../shadow-cljs/target/classes"]

thheller19:04:34

in your project I mean

thheller19:04:16

then it'll just use whatever the current state is, no need to build a jar

seanstrom19:04:53

That’s great! I’ll give that go

thheller19:04:33

it is this change I'm fairly certain

thheller19:04:01

that used to add a wrapper for code to pull in all the "locals", so it didn't use any globals

thheller19:04:18

but now it uses globals. I haven't decided how to handle this

thheller19:04:43

you didn't answer why you are not using :esm? 😛

thheller19:04:11

it doesn't have all the old random hacks that :npm-module has, so it should be preferred

seanstrom19:04:19

The compile output for esm can’t be hot reloaded atm, the source is reloaded and throws an error. I would like to use esm but I don’t think it works with the hot reload.

seanstrom19:04:53

The esm target throws an exception related to namespace already being defined by goog

seanstrom06:04:31

Okay here’s an another version of the project but with ESM setup: https://github.com/seanstrom/cljs-dev-template/tree/debug-build-esm

seanstrom06:04:56

This is the kind of error I get when I hot reload: Error: Namespace "goog.asserts" already declared.

thheller08:04:31

ok, it is time you need to explain what your goal is with all of this. you are using two build tools, each trying to provide basically the same features. and they are interfering with each other big time.

thheller08:04:59

I get a couple of problems, the already declared only being one of them

thheller08:04:38

the problem is parcel hot-reloading way too early, then shadow-cljs trying again

thheller08:04:03

parcel even tries to reload sometimes while shadow-cljs is still writing the file, so I get EOF errors

seanstrom08:04:28

The goal is to use a JS bundler for bundling JS dependencies and watching reloading JS and CSS. I don’t want to deal with JS externs and other issues with importing JS dependencies through the normal build pipeline, and I want to hot reload css changes.

seanstrom08:04:12

Yeah I gave up on using Parcel because I cant prevent it from watching the CLJS output. I pushed up a working build configured with the ESM output from Shadow and Vite.

thheller08:04:35

that sounds like what you want?

thheller08:04:55

> I don’t want to deal with JS externs

thheller08:04:13

none of that eliminates the need for externs? in fact you'll need MORE if shadow-cljs isn't processing npm dependencies?

thheller08:04:26

> I want to hot reload css changes.

thheller08:04:29

shadow-cljs can do that on its own?

seanstrom08:04:44

What about importing svg files? or Sass files, or any other media types that the JS tooling supports?

seanstrom08:04:20

The point is to use the JS tooling for pieces of the JS ecosystem that I can’t get from Shadow

seanstrom08:04:06

And I want to shadow to manage the CLJS stuff because it basically the only option for compiling stuff with hmr (outside of Figwheel and building my own).

seanstrom08:04:26

Ideally I would have a cljs to esm compiler, with repl and hot-reloading support. And that should be able to work as along as I don’t use to hot reloaders for the CLJS stuff

seanstrom08:04:09

Because it seems that way shadow hot reloads stuffs is more specific than just a re-eval the whole file right?

thheller08:04:14

:target :external sounds like what you want here?

thheller08:04:31

just found a bug in :esm, but thats beside the point

thheller08:04:55

your template seems to work, just need to update shadow-cljs. template is using 2.22.0, 2.22.9 works

thheller08:04:31

that explains how it reloads stuff

seanstrom08:04:22

Version 2.22.9 does not work for me with the Parcel JS configuration. It works only if I add the extra window.goog = $CLJS.goog to it.

thheller08:04:09

I only tested vite now?

thheller08:04:44

but literally. your use case sounds exactly like what :js-provider :external is designed to do

thheller08:04:55

you don't lose anything. you only lose headaches because vite/parcel/whatever don't need to process the CLJS output

seanstrom08:04:52

js-provider :external would only configure the CLJS output to represent JS dependencies as require expressions right?

seanstrom08:04:26

That would happen when I import stuff into CLJS that’s JS right? If I have to independent systems of JS and CLJS I need a bundler that works with both. The viewpoint here is that it’s a headache to do JS stuff in the CLJS tooling without going all-in on the CLJS way of doing stuff.

thheller08:04:40

it does not no

thheller08:04:31

but yes, the js bundler will only see JS dependencies

thheller08:04:35

it will not touch CLJS code at all

thheller08:04:03

the only thing you can't do is access the CLJS code from JS

thheller08:04:26

the CLJS code can access JS just fine, but it is one way

thheller08:04:47

well, if you like hacks you can make it both ways but I wouldn't recommend that

seanstrom09:04:57

I don’t think we’re talking about the same stuff. Look at the original example, it broke because the semantics of npm-module hot reloading has changed right?

seanstrom09:04:11

It used to be scoped but not it relies on globals instead right?

seanstrom09:04:30

I came here looking to fix that

seanstrom09:04:01

I can add this snippet: window.goog = $CLJS.goog somewhere in my file, but that seems like a hack

seanstrom09:04:03

So im trying to avoid my builds exploding because the outputs from CLJS don’t quite mesh well with every other JS bundle tool because of google closure stuff and side effects for declaring namespaces inside of all the modules

seanstrom09:04:10

No matter how many ways I can configure :js-provider :external that doesn’t solve the issue of organizing a polyglot project

seanstrom09:04:19

I need both JS and CLJS to play nice

seanstrom09:04:51

That’s a reasonable take when you build both systems actively

seanstrom09:04:46

It seems that the best way to avoid headaches is to disable Vite’s (or Parcels) HMR for any CLJS assets. How is that a hack?

thheller09:04:29

ok, so vite now works perfectly fine for me

thheller09:04:40

just was way slower than it needed to be but I fixed that

seanstrom09:04:55

Did you make a change to the project configuration?

thheller09:04:11

well, updated to 2.22.9

seanstrom09:04:33

The vite was working for me with 2.22.0, but I’ll also try 2.22.9 just to confirm. Would you say this is a hack when: • Shadow controls all the CLJS reloading still • Vite only controls unrelated CSS and JS stuff that isn’t being touched by the CLJS

thheller09:04:29

no, not a hack. that is a supported use case, which may have rough edges since not many people do this

seanstrom09:04:35

yup yup, I can feel the bleeding edge at this point 😅

seanstrom09:04:47

I can confirm that 2.22.9 also works for me

thheller09:04:09

:js-options {:js-provider :import} you need to add to the build config once you want to start using actual JS code

thheller09:04:16

since otherwise shadow will still try to bundle on its own

seanstrom09:04:30

Ahh good call, ill add that

thheller09:04:58

vite config you may want to change to ignored: ['**/cljs-dist/cljs-runtime/**'],, so it'll still pick up new npm depencencies and such

👍 2
thheller09:04:13

and let me make a new release so everything isn't so horribly slow

seanstrom09:04:07

Nice, thank you!

seanstrom09:04:30

I guess the bonus here is that I’m using the esm format now, so I don’t have that npm-module bug

seanstrom09:04:37

Do you still want help fixing npm-module goog thing?

thheller09:04:31

the problem is that :npm-module is using the same shadow.cljs.devtools.client.browser namespace as the :target :browser does

thheller09:04:44

but they are fundamentally incompatible with each other in how they need to reload stuff

thheller09:04:27

so I'd like to avoid having a bunch of if in that ns, checking what it is supposed to do

thheller09:04:06

but I guess thats what is needed

thheller09:04:24

try 2.22.10 btw, :esm much faster

seanstrom09:04:25

Nice! just try it and works like a charm 🎉

seanstrom09:04:56

Will you be able to completely remove npm-module at some point?

thheller09:04:21

no reason to break people using it, so no need to remove it

thheller09:04:40

but I always try to steer people away from it because its use is pretty niche

thheller09:04:03

I still want to fix it regardless, stuff that exists should work after all

thheller09:04:44

I like that vite only touches the import paths and nothing else it seems. that is ideal as far as shadow is concerned

seanstrom09:04:26

Good to know!

seanstrom09:04:33

Overall, thanks for the help, and sticking through to confirm that this new solution seems more maintainable

seanstrom09:04:22

For the npm-module stuff, do you think it would be easier to include all of the window.goog = $CLJS.goog inside of cljs_env.js ? I think before it was inside each script when reloaded, but now it’s the minimal amount of JS update the namespace

thheller09:04:39

window.goog = $CLJS.goog is not enough, don't get hung up on that

seanstrom09:04:02

i meant all of the possible one’s inside the script? or are they different per script?

thheller09:04:07

its just the first you run into, so it doesn't get to the other errors later on

thheller09:04:04

ok, long story: once upon a time I decided to try to make :npm-module be a good citizen and NOT expose any globals whatsoever. how then do you make sure the REPL and hot-reload and CLJS in general still work, since everything in that world assumes to live in the same global scope. so in my younger days I decided to be clever and recreate and fake that "global" scope in every single namespace

thheller09:04:14

thats what all the var goog = $CLJS.goog stuff is

thheller09:04:34

it turns out this was a terrible idea, and not actually useful and gets in the way more than I'd like

seanstrom09:04:17

hmm okay, so we want to avoid that fake global stuff and just define all the global on window?

thheller09:04:41

so instead, everything should live in global during development. and only be local in release builds

thheller09:04:08

but that also doesn't play with certain tools, such as parcel which seems to have a weird notion about what global means

thheller09:04:29

in :esm it just uses globalThis which is sort of standard and I guess respected by vite

thheller09:04:46

so, everything just works as expected

thheller09:04:25

so the choice I have to make in :npm-module is a) remove all the fake-global stuff or b) keep the hacks and "fix" the hot-reload problem browsers now have

thheller09:04:41

I'm leaning towards a since it removes so many headaches

thheller09:04:32

but it is basically rewrite everything :npm-module related type of thing, and given how many hacks that has I'm not sure it will not break in unexpected ways in other peoples builds actively using it

thheller09:04:10

I'm trying to avoid breaking changes at all costs, but :npm-module is giving me nightmares at this point. so I guess I might need to do this at some point

seanstrom09:04:15

Well if we had a global object will all of the definitions you’d want on global, you could pass that into the script eval as the context?

thheller09:04:17

the eval already happens in the global scope, that is precisely the problem after all. it doesn't see the fake global properly, so there are two worlds. one that is fully initialized on startup, one that is partially broken on hot-reload

seanstrom09:04:08

yup yup, and I’m thinking that when we know something is “reloading”, we can reference the fake global inside script evals arguments

thheller09:04:13

:npm-module also had a bunch of node specific things baked in, which always get in the way when not actually building for node

seanstrom09:04:14

let me double check if that’s supported

thheller09:04:55

you can't fake a global in eval, can't control how the browser resolves stuff after all

thheller09:04:14

:npm-module has been by far the most problematic targets of any of them. thats why I hate touching it so much, fix one thing always breaks another 😛

seanstrom09:04:44

you’re right, i was imagining the node version of some eval stuff

thheller09:04:47

and all only for trying to avoid the global scope 😛

thheller09:04:22

the cleanup should make things much nicer, just need to find and revert all the hacks first

seanstrom09:04:34

Okay so what’s our best guess as to why the global stuff is incorrect, because Parcel overrides the global?

thheller09:04:48

no because of the commit I linked

thheller09:04:05

before that change the server used to wrap the output it generated for hot-reload

thheller09:04:27

so it would append the whole var goog = $CLJS.goog; stuff

seanstrom09:04:10

Yup exactly, though I thought you were saying that window.goog should already be the same as $CLJS.goog?

thheller09:04:11

but I only changed how reloading works in the new shadow.cljs.devtools.client.npm-module (intended for node), and not the shared browser ns

thheller09:04:52

yes, because when the code was initially loaded everything was properly on the $CLJS global object

thheller09:04:01

but hot-reload wasn't. thus stuff starts to break

seanstrom09:04:29

I see what you mean, during load stuff uses $CLJS right?

seanstrom09:04:42

but reload it uses global

thheller09:04:07

yes, during normal loading it avoids global completely except for one actual global $CLJS which is used to bridge stuff

thheller09:04:44

but after that shadow.cljs.devtools.client.browser just assumes everything is global, because in all other targets it is used in it is

thheller09:04:59

just :npm-module is a special case

seanstrom09:04:31

ahhh so this extra tricky because you’re trying to remove this fake global stuff, but some code still uses it, and new code depends on it not being there at all.

seanstrom09:04:13

Is it possible to have a separate shadow.cljs.devtools.client.npm-module-legacy file that adds back scoping stuff?

seanstrom09:04:00

I would lean towards what you recommended earlier about removing all the fake global stuff and maybe just using globalThis, but that would seem to break anyone using the npm-module stuff right?

seanstrom09:04:17

At least if they depend on global stuff never being touched

thheller10:04:15

can't reliably use globalThis since it might not be supported in all environments but can probably test for it and fallback for global if it doesn't exist

seanstrom10:04:50

Does using globalThis in the browser for an npm-module target work?

agorgl14:04:16

Hello there! I'm building my (template) project skeleton in a monorepo fashion that will have clj/cljs sources under the src/{clj/cljs} folders. I'm using the shadow-cljs feature that reads the dependencies from deps.edn, and it worked well for development by defining a :cljs alias with my clojurescript dependencies and referencing it with {:deps {:aliases [:cljs]} in my shadow-cljs.edn. The problem is that when I also added a clojurescript build step in my build.clj using the shadow-cljs api (shadow.cljs.devtools.api/release app) the :build alias could not find my clojurescript dependencies. What would a proper solution be? • Should I move all clj and cljs dependencies in the root :deps of deps.edn and use {:deps true} in shadow-cljs.edn? Are there any pitfalls or reason I should avoid this? • Should I duplicate the cljs dependencies in both :cljs and :build aliases?

thheller14:04:10

no clue. never used tools.build yet. can't you just activate the cljs alias yourself when calling the build task?

thheller14:04:31

but yes, cljs compilation needs to have the dependencies available, which build tasks by default do not have

thheller14:04:12

another option is to shell out to run shadow-cljs via command line, instead off the CLJ api

agorgl14:04:12

yes I thought of the third one too, its a shame that the api does not read the shadow-cljs.edn config

thheller14:04:08

thats not how this works at all though. it absolutely reads the shadow-cljs.edn config. it just doesn't start a new JVM, since it was never designed for tools.build or how that decided how to do stuff

thheller14:04:26

FWIW tools.build starts a new JVM for CLJ compilation as well. just have to do the same for CLJS.

agorgl14:04:55

ah yeah this makes sense

thheller14:04:24

but how are you running the build task in the first place? isn't it something like clj -X:foo?

agorgl14:04:37

I'm incorporating the shadow-cljs phase into my uberjar phase and I'm calling it like clj -T:build uber

thheller14:04:56

an clj -T:cljs:build uber doesn't work?

agorgl14:04:58

I could always do something like clj -A:cljs -T:build uber

agorgl14:04:44

I believe it works, I'm trying to keep as clean as possible the project build commands, just experimenting a bit

thheller14:04:13

this is how tools.build runs CLJ compilation

thheller14:04:24

so you can start a new JVM that way for CLJS as well

thheller14:04:32

just needs a helper function I guess

agorgl14:04:36

woah I did not know that tools.build shells out

agorgl14:04:08

I might as well just shell out to npx shadow-cljs release app too

thheller14:04:57

or rather just :main-args ["-m" "shadow.cljs.devtools.cli" "release "app"]

thheller14:04:10

but seems easier to shell out 😛

thheller14:04:31

or ... to be perfectly honest just call npx shadow-cljs release app FIRST

thheller14:04:44

then just build you uber separately, with the files shadow-cljs already built 😛

agorgl15:04:27

Ok I tried to shell out, and (b/process {:command-args ["npx" "shadow-cljs" "release" "app"]}) gives:

shadow-cljs - config: /home/user/workdir/webapp/clj-fullstack/shadow-cljs.edn
shadow-cljs - starting via "clojure"
Execution error (FileNotFoundException) at clojure.main/main (main.java:40).
Could not locate shadow/cljs/devtools/cli__init.class, shadow/cljs/devtools/cli.clj or shadow/cljs/devtools/cli.cljc on classpath.

Full report at:
/tmp/clojure-15436134260688801745.edn

thheller15:04:36

that means the the shadow-cljs dependency was missing in deps.edn

agorgl15:04:03

ah damn, i didn't test it standalone sorry

thheller15:04:04

or the alias not activated. make sure it is in the :cljs alias and :deps {:aliases [:cljs]} in shadow-cljs.edn

agorgl15:04:23

Yep everything works fine now

agorgl15:04:32

I think I'm gonna go with that, thanks!

agorgl15:04:14

Another quick question: Why is the shadow-cljs dependency needed in :cljs alias? I thought that when using the shadow-cljs wrapper/binary it injects it itself, although I may need to rtfm again

thheller15:04:07

no, it never injects itself

thheller15:04:32

it used to, but that is just more trouble than its worth.

thheller15:04:49

just better to be in there all the time so all other deps.edn related things can also see it

thheller15:04:38

well it adds itself when using shadow-cljs.edn :dependencies, but not deps.edn or project.clj

agorgl15:04:57

Ah now it makes sense

agorgl15:04:18

Final question and I'm done bothering you with and your awesome project 🙂

agorgl15:04:33

How the version in devDependencies package.json affects the whole process?

thheller15:04:15

when using deps.edn not at all

thheller15:04:39

well, it still controls which version of the shadow-cljs command you get

thheller15:04:03

so it should still be somewhat close to the version in deps.edn, but the version in deps.edn decides all compilation stuff

agorgl15:04:53

Ah I got it

agorgl15:04:57

I suppose I could remove it completely too if I went with clj -M:cljs -m shadow.cljs.devtools.cli release app

thheller15:04:31

in theory yes, although I still recommend installing it as that will still provide other npm packages you may need when using shadow-cljs. i.e. the ws package when building node stuff

thheller15:04:38

frankly I don't see the point in wrapping one build tool in another, so I would always use shadow-cljs as a separate step

agorgl15:04:17

Well in the end you are right, but through this process I get to understand shadow-cljs build process a little better

Braden Shepherdson16:04:00

I'm having trouble with methods on a defclass object. I want to create a JS class that gets called from JS with the usual foo.bar() style, but I get

(defclass Foo
  (constructor [_this])
  Object
  (bar [_this] 17))

(let [foo (Foo.)]
  (.bar foo))
;=> TypeError: foo.bar is not a function
I'm not sure what I'm missing - this seems like it should work and the https://github.com/thheller/shadow-experiments/blob/master/src/main/shadow/experiments/grove/ui/vlist.cljs#L26 I found seem to call Object methods that way.

Braden Shepherdson17:04:49

ah, to avoid the XYZ problem: what I'm really trying to do is create a subclass of Object like this

class Foo {
  constructor(guts) {
    this.guts = guts;
  }
  raw() {
    return this.guts;
  }
}
if there's an alternative (something with deftype?) that would be fine too.

thheller17:04:57

either should work? deftype and defclass

thheller17:04:07

your code looks fine?

Braden Shepherdson17:04:43

hmm. I just got it to work with a basic deftype; I suppose that solves my problem. but I couldn't get the defclass to work.

thheller17:04:02

and your example code works for me?

thheller17:04:13

> (require '[shadow.cljs.modern :refer (defclass)])
nil
cljs.user=> (defclass Foo
  (constructor [_this])
  Object
  (bar [_this] 17))
#object[Function]
cljs.user=> (let [foo (Foo.)]
  (.bar foo))
17
cljs.user=>

thheller17:04:19

in the browser repl works just fine

Braden Shepherdson17:04:38

weird. I'm running a node REPL via Conjure, but that shouldn't break this?

thheller17:04:17

nope, should be fine. maybe you have some old Foo reference somewhere?

Braden Shepherdson17:04:46

that's probable, I suspect I broke my REPL state with an early attempt.

thheller17:04:15

it does indeed fail in node

thheller17:04:19

not sure why though, let me check

Braden Shepherdson17:04:53

(it seemed like there was no prototype at all. (.-prototype Foo) ;=> {})

Braden Shepherdson17:04:10

no rush; I'm unblocked by using deftype for this simple case.

thheller17:04:04

hmm yeah I don't get it. generated code looks fine

Braden Shepherdson17:04:17

huh, that's a strange gap. Node is V8 (right?) so I don't see where the gap would be. something about exporting, maybe?

thheller17:04:38

did you only try this in the REPL? it works fine via load-file

Braden Shepherdson18:04:27

Yeah, only the REPL.

Rambabu Patina17:04:28

Hi, I am facing an issue with loading our own react components which has newer syntax features such as Object.assign, Object destructuring, spreading, and the nullish coalescing operator. I see the polyfills discussion on https://github.com/thheller/shadow-cljs/discussions/827 and tried with :output-feature-set :es8 but still having the same issue. Here is the dependency versions, shadow-cljs version: 2.16.12 com.fulcrologic/fulcro version: 3.4.14 react version: 16.13.0 Error in console,

Uncaught TypeError: fulcro_xxxxkit.xxxxkit.ui_box is not a function
    at eval (xxxxx_xxxxxx.ui.pages.xxxxx_xxxxx.js:sourcemap:68:477)
    at $fulcrologic$fulcro$components$wrapped_render [as wrapped_render] (com.fulcrologic.fulcro.components.js:1547:111)
    at Function.xxxxx_xxxxx$ui$pages$xxxxx_xxxxxx$render_Root (xxxxx_xxxx.ui.pages.xxxxx_xxxxx.js:sourcemap:66:42)
Please suggest how to resolve it. Your help will be greatly appreciated. Thanks

thheller17:04:14

is that the only error? otherwise impossible to say without seeing code. I mean it says its not a function, did you check what it is? if it actually exists or so?

thheller17:04:48

go in the browser console and type fulcro_xxxxkit.xxxxkit to inspect the "object"

Rambabu Patina17:04:58

Yes you are right, it returns empty object

Rambabu Patina17:04:08

though we have wrapped the component in fulcro

Rambabu Patina17:04:40

I am wondering it works for other components such as fulcro_xxxxkit.button

thheller17:04:08

can't see your code so can't comment much

Rambabu Patina09:04:56

Hi @thheller, Here is my code testkit.cljs where wrapped with fulcro

(ns fulcro-atlaskit.testkit
  (:require
   ["@testkit/react" :refer [Box GlobalHeader]]
   [com.fulcrologic.fulcro.algorithms.react-interop :as react-interop]))

(def ui-box (react-interop/react-factory Box))
(def ui-global-header (react-interop/react-factory GlobalHeader))
And consuming Here
(ns xxxxx-xxxxxx.ui.pages.xxx-xxxx
  (:require
   ..
   [fulcro-atlaskit.testkit :as testkit]))

(defsc Root [this _]
  (dom/div
    :.container
    (dom/h1 (tr " Get started"))
    (testkit/ui-box)))

thheller09:04:05

looks fine to me

Rambabu Patina17:04:34

I appreciate your help @thheller , I will debug in the way why getting empty object.