I am losing my mind over this. Accessing an attribute via interop fails when I build the project, but not during development. How is that even possible?
(prn atom)
;; #js {:resn "MET", :x -122.012, :y 27.877, :z 65.953, :elem "C", :hetflag false, :altLoc " ", :chain "A", :resi 1, :icode " ", :rescode "1", :serial 2, :atom "CA", :bonds #js [0 2 3], :ss "c", :bondOrder #js [1 1 1], :properties #js {}, :b 50.91, :pdbline "ATOM 2 CA MET A 1 -122.012 27.877 65.953 1.00 50.91 C ", :index 1, :reschain 0, :style #js {:cartoon #js {:colorfunc #object[Function]}}, :color 13158600, :model 0, :capDrawn false}
(prn (js->clj atom))
;; {"bonds" [0 2 3], "properties" {}, "z" 65.953, "pdbline" "ATOM 2 CA MET A 1 -122.012 27.877 65.953 1.00 50.91 C ", "model" 0, "index" 1, "serial" 2, "ss" "c", "x" -122.012, "rescode" "1", "bondOrder" [1 1 1], "reschain" 0, "resi" 1, "style" {"cartoon" {"colorfunc" #object[Function]}}, "atom" "CA", "b" 50.91, "y" 27.877, "icode" " ", "resn" "MET", "hetflag" false, "altLoc" " ", "elem" "C", "color" 13158600, "capDrawn" false, "chain" "A"}
(prn "atom resi" (.-resi atom))
;; nil
It's probably a release build, and as such it probably uses advanced optimizations that rename some attributes.
Try (.-resi ^js atom).
If you use shadow-cljs, you should see a warning in the build output saying something like "the type of target in (.-resi atom) could not be determined".
But there are rare gaps where the warning doesn't appear.
And of course you don't have to slap ^js everywhere. Just in front of the introduction of the symbol is enough.
Now that I think about it it seems almost obvious the problem had to be something like that. Thanks!
Alternatively, if atom (BTW shadowing built-in names should be avoided) is considered "data" and not "code", then you might want to use things like unchecked-get or goog.object/get.
I am having another issue where something works in dev but not prod where weirdly all the method calls work, accept the .removeAllShapes one.
It does exist alright (https://3dmol.org/doc/GLViewer.html#removeAllShapes)
Probably this is also made worse because I am using an external bundler?
But in any case, no annotation did the trick here:
(I am aware this might be better done with a ref and all
:reagent-render
(fn [& {:keys [objects style]}]
(when-let [^GLViewer viewer @viewer-state]
(let [{:keys [spheres boxes]} objects]
(.removeAllShapes viewer) ;; <-- this is broken
(doseq [sphere (filter some? spheres)]
(.addSphere viewer (clj->js sphere)))
(doseq [box (filter some? boxes)]
(.addBox viewer (clj->js box)))
(doto viewer
(.setStyle (clj->js {}) (clj->js style))
(.render))))
[:div {:class "mol-container"
:style {:width "450px"
:height "450px"
:position "relative"
:border "solid grey 1px"}
:ref ref}
"Loading viewer..."])Try adding (js-debugger) before the form that doesn't work and see what the code actually is when execution gets to that point in a build that doesn't work.
Alternatively, use shadow-cljs release app --debug (`app` is your build ID) - it will pretty-print the code and use pseudo-names instead of full renames, so you will be able to trace what's going on.
That's just to confirm whether .removeAllShapes gets renamed or not.
Thanks! I will look into that in a bit
When debugging in the --debug build it throws errors on all methods. When not using --debug it only throws on the .removeAllShapes This is very spooky really
No clue. Can you create an MRE?
[^GLViewer viewer @viewer-state] this is wrong and the cause of all your troubles ๐ the only tag you should ever consider using is the ^js tag. everything not using ^js or ^js/GLViewer will be treated as "safe to rename"
I wish it was. It makes no difference if its there or not - I did also try with ^js too
well, I can guarantee that it will definitely not work with ^GLViewer. ^js we could debug further ๐
this is also incorrect use of reagent and non-react DOM things. modern react should be using useEffect for that interop thing. dunno about that old reagent style
> everything not using ^js or ^js/GLViewer will be treated as "safe to rename"
Huh. Even when GLViewer is something that has been imported?
fml
I was so sure I had tried it with ^js too
@p-himik there is no such thing as "imported" as far as type hints are concerned, thats why the only type hint that makes any sense is ^js or if you are really obsessed ^js/GLViewer. the js part is the only relevant bit though.
basically if you ever get a inference warning use ^js, don't complicate it further. not worth worrying about finer details unless you want to tweak the final build size by 0.05% or so ๐
Okay that helped with my immediate problem. About the incorrect usage part - I am a bit out of my waters doing this whole frontend stuff rn, as in it feels like I am trying to cross the pacific in a kayak. Since the component for now "does what its supposed to" I guess I'll revisit that when I'm feature-complete
@d.eltzner I made an example using https://github.com/thheller/reagent-pdfjs/blob/master/src/main/demo/app.cljs a long while ago. basically you can adapt this to whatever this GLViewer does, moving the render bits into the useEffect. basically create a ref and use that to get the DOM node you want to embed something else in.
I'm sure there are better docs for this in the reagent docs somewhere
@thheller I have a vague recollection of you writing somewhere that tagging symbols with actual "types" is useless right now (well, except for the js bit, yes), but might be useful in some potential future that could never come - if GCC starts supporting some required parts.
If I'm not hallucinating, wouldn't that also require tags being aware of imports/requires? So that ^GLViewer is the GLViewer and not some plain symbol.
not saying inference can't be improved. just referring to the current state, which I don't expect changing much. certainly not impossible to resolve tags, just not done currently.
I'm new to clojurescript. I'm using nbb. This code works and logs output to console but how do I wait/return the JSON in the promise?
(let [client (new s3/S3Client)
req (new s3/ListBucketsCommand #js{})]
(.then (.send client req)
#(js/console.log %)))I don't know about nbb but in general - you don't. You pass the next callback that does what you want.
If you want the value to be available in the REPL, you can use an atom.
If you want to access the value from some user of the above code, that user has to provide a function. Or use some sugar like Promesa or one of the variants of js-await macros you can find online to make it look like there's no function.
Promesa is built into nbb
I'm trying to use promesa and can print the result but not sure how to return it:
(let [client (new s3/S3Client)
req (new s3/ListBucketsCommand #js{})]
(p/let [foo (.send client req)]
(prn foo)))See the nbb channel for a similar discussion. You canโt break out of promises, you can only chain them
Learning about how promises work fundamentally may help
ok, thanks!
Iโm trying to include https://github.com/maxGraph/maxGraph as a dependency in my clojurescript app. When shadow-cljs compiles, I get the error
Errors encountered while trying to parse file
/Users/markaddleman/IdeaProjects/maxgraph-demo/node_modules/@maxgraph/core/lib/index.js
{:line 113, :column 9, :message "'from' expected"}
Details in threadLine 113 is
export * as constants from './util/Constants';
which grok tells me is valid for ecmascript 2020My shadow-cljs is
{:source-paths ["src"]
:dependencies []
:builds
{:app
{:target :browser
:output-dir "target/public/js"
:asset-path "js"
:modules {:main {:entries []}}
:compiler-options {:language-in :ecmascript-2020}}}}
Is this error coming from the clojurescript compiler or something else? If from the cljs compiler, are there other config knobs to get it to understand this syntax?
If itโs relevant, Iโm including maxgraph in my code using
(ns
(:require ["@maxgraph/core" :as maxgraph]))
ah. Thanks ๐
if you must use that package you'll need to switch to :js-provider :external https://shadow-cljs.github.io/docs/UsersGuide.html#js-provider-external
Oh, cool. Thereโs a workaround.
Iโll check it out. Very much appreciated!
Is there anyway to "override" clojurescript's do in a namespace with my own custom fn/macro and then still use regular do in that namespace?
With a regular function like mapv I would just do this
(ns my-ns
(:refer-clojure :exclude [mapv])
(:require
[clojure.core :as c]))
(defn mapv ...)
Then I could still use mapv by doing c/mapv
But with do the docs say it's special form and c/do isn't defined
Is there some way to get around this?You can potentially do it but that would require changing how CLJS works, AFAICT. Why would you want something like this?
I'm writing some code to help with promises and wanted to have a custom macro for it named do, I can just name it do!but I was curious if it was possible
I see that promesa is able to have a custom do but they don't use clojure's do in the same namespace
You can mimic the built-in do with e.g. (let [] ...).
thanks! I might do that
Alternatively, shift the declaration of your own do to the very end of the namespace. But you'd have to be careful and document that it should not become higher.
oh interesting, I'll try that, I would think that I would get compiler warnings from it
it does seem to work!
oh wait, I need to reuse it in this namespace lol
but, I could just have two macros
Or move the usages below the definition. Unless the same top-level form uses both your custom do and the built-in one.
And maybe (declare do) would work as well, I actually don't know.
A symbol like do is often referred to as a special form, but more pedantically the special form is (do ...) meaning any seq starting with the symbol do, and often lisps will ignore any other bindings for those names that start their special forms, it depends if they check for the name being bound first, or check for special forms first in their implementation. I am not sure what clojurescript does, but it would not surprise me if you can define a macro with a name that is the same as a special form, but it doesn't let you use it
https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/analyzer.cljc#L4118 looks it checks for a special form and does that before falling back to looking up stuff in the environment
So you can never have a (do ...) where do is not the built in special form, stuff like (somens/do ..) seems like it would work
For the most part my special do will be used outside of the namespace it's defined in, something like mp/do. I settled with defining a do! macro at the top of the file and then do (which just calls do! at the bottom of the file
This makes me wonder then, in promesa, they define a special do macro and then also refer clojure exclude do, but they use do in 1 place https://github.com/funcool/promesa/blob/11.0.671/src/promesa/core.cljc#L658
so which do is being used?
Seems like from the code you posted @hiredman, that even though do is excluded, that cljs.core/do is being used
There are layers, because it is in syntax quote the symbol gets namespace qualified by the reader
To me there are enough edge cases around using the same name as special forms that I would just avoid it
(although after saying that, syntax quote may also treat special forms specially)
normally I would avoid this problem as well but I thought I would ask and see if I can learn something.
plus as an API which someone might consume if I release this as a library do is a little more clear than do!
I think you're right that it's the clojure do, looks like promesa fully qualifies their do inside of macros https://github.com/funcool/promesa/blob/11.0.671/src/promesa/core.cljc#L586 (I'm also a macro noob, maybe this is totally normal to do anyway)
> it checks for a special form and does that before falling back to looking up stuff in the environment
@hiredman It's the opposite. The linked function, analyze-seq*, is used in this context:
(let [mform (macroexpand-1 env form)]
(if (identical? form mform)
(analyze-seq*-wrap op env form name opts)
(analyze env mform name opts)))
So macros specifically are expanded before special symbols are checked.