Trying to compile cljs into a single js file, but it is failing to pull npm dependencies into the file and eliminate dead code
I have an extern.js file that includes function names and have a shadow-cljs.edn but neither of them are bundling everything
you did not provide enough information to help. which command did you run? what is the build config? which :target
?
do I need webpack for this? seems like there would be a lot of redundant code would be packed which the closure compiler should eliminate
so I have a lein CLJS project and want to depend on the JavaScript version of the protobuf runtime library. that's the NPM package google-protobuf
, which ships Closure code that should fit nicely in CLJS code.
but I'm not sure what's the most practical way to include that in a build.
Hello, I have a lein project using shadow-cljs
, and it works great. I have one question regarding npm dependencies:
Context: I started a CLJS prototype to evaluate for the NextCompanyProject™ by using Emacs, but now I also tried setting up IntelliJ with Cursive to see how that works (and to sell it more easily to devs in my team).
Cursive and IntelliJ work pretty well out of the box in this case, I just have one minor annoyance, which leads me into the actual question.
I tried a couple of things from shadow-cljs docs and Cursive docs, I for example generated a pom.xml
file with lein pom
, but I suppose this doesn't help for npm dependencies?
Question: Is it possible, given the context above, to have npm dependencies not complain about not being able to resolve when using IntelliJ with Cursive?
For reference, I would have something like this in the require:
["@material-ui/core" :as ui :refer (IconButton Fab)] ; ui, IconButton and Fab cannot be resolved in this example
Sadly, no, although I’m planning to fix this soon. I had hoped that this would make it into the next release but it’s looking like I will have to bump it. However in the next release there will be some changes so that at least it doesn’t shout at you.
@cfleming It actually helps me a lot just knowing something is in the pipeline and that I can stop trying to solve the problem. I haven't used Cursive for very long yet, but from what I've used it's working really well, thank you for your work!
No problem, I’m glad it’s helping!
I can't find a good reference on what happens when I tell CLJS :compiler {:libs ["some/dir"]}
- does it look for all .js files in that directory and take their goog.provide
namespaces as gospel? does the folder structure have to match up a la Java?
I'm getting /path/to/my/project/target/public/cljs-out/dev/somefile.js is not a relative path
and I'm not sure what that's trying to say - the file does actually exist, though the path is certainly not relative.
Heya, I think I've found a bug with js->clj
. If you try to convert {"constructor": true}
with js->clj
, you get #object[Object [object Object]]
instead of a map. Converting it with transit will get you a map though.
;; src/buggy_js_clj/core.cljs
(ns buggy-js-clj.core
(:require [cognitect.transit :as t]))
(let [content #js {:constructor true}]
(print "Parses correctly with js->clj?"
(-> content
(js->clj :keywordize-keys true)
map?)
;; false
)
(print "Parses correctly with transit?"
(->> content
(js/JSON.stringify)
(t/read (t/reader :json))
map?)
;; true
))
;; deps.edn
{:deps {org.clojure/clojurescript {:mvn/version "1.10.764"}
com.cognitect/transit-cljs {:mvn/version "0.8.264"}}}
;; running repro from cli
;; clj -m cljs.main --target node --output-to main.js -c buggy-js-clj.core && node main.js
hey, js->clj
has a long list of known issues that can't really be fixed without breaking others relying on certain behaviour. if you know you have only JSON data then it is advisable to stay away from js->clj
completely.
instead you can use something much simpler like https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/json.cljs#L4
cljs.user=> (require '[shadow.json :as j])
nil
cljs.user=> (j/to-clj #js {:constructor true})
{:constructor true}
ran into this because one of our JSON returns from an API suddenly stopped parsing via js/JSON.parse
followed by js->clj
, and narrowed it down to that key
is the best place to report this sort of thing still the JIRA?
best place to report issues is on https://ask.clojure.org
unless you have a jira acct, in which case that's ok too
I have a JIRA acc actually, might as well open it there in case I manage to submit a patch for it then
hm wonder if it's related to https://clojure.atlassian.net/browse/CLJS-3201
Given the Mike's comment, doesn't seem like it. In the case of js->clj
the culprit is the way the type
function is implemented.
here is the JIRA issue https://clojure.atlassian.net/browse/CLJS-3285
could you add that comment in the JIRA issue?
sounds like it would be helpful for whoever ends up looking at the issue
I don't have an account. But I was able to find that out in ~15 seconds so I don't think that would be much of a help anyway. :)
The js->clj
implementation is very straightforward.
I'll add a comment there saying what you said, I found it helpful
To be more concrete: (assert (true? (type #js {:constructor true})))
.
hmmm it looks like type
should be checking it's a function
so instead of
(defn type
"Return x's constructor."
[x]
(when-not (nil? x)
(.-constructor x)))
maybe it should be
(defn type
"Return x's constructor."
[x]
(when (and (some? x)
(fn? (.-constructor x)))
(.-constructor x)))
Well, then you should expect that (js->clj #js {:constructor #()})
will still be broken.
hm...
my expectation is that a js map containing the constructor
key is converted into a cljs map... but to be honest it's not very clear to me how the full breadth of scenarios should behave
hrm I don't know about this
using constructor
is problematic because JavaScript
but if you have solution that doesn't create more problems will take a look
but my first thought is don't do that
I think transit works here because it's data exchange only
my concrete situation is that I have data stored in Firebase containing string keys chosen by users
when downloading and parsing it, I run into this
if you look at js->clj
you should see you have options
IEncodeClojure
is a thing
you could extend to that on the fly for your stuff to avoid this
I wasn't aware I had that option, it does sound good for my case
thanks for bringing it up
yeah there needs to be trapdoor for some cases
I'm switching that issue to minor for now unless you discover for some reason that approach doesn't work for you
SGTM
wasn't really sure what the correct priority was, it defaulted to major so left that
which is fine, can always adjust priority later
@dnolen I don't think that extending the IEncodeClojure protocol in that case works, because (extend-type true ...)
throws a compilation error:
Unexpected error (ClassCastException) compiling at (REPL:1).
java.lang.Boolean cannot be cast to clojure.lang.Named
but I think I can instead convert the JS data itself into map entries, falling into the previous cond
(map-entry? x)
(MapEntry. (thisfn (key x)) (thisfn (val x)) nil)
either that or removing that entry and adding it later manually...
extend-type true
isn't right
use (specify! ...)
on the value directly
specify
is probably good enough too?
actually you do need specify!
because your value isn't ICloneable
(defn fix-json [json] (specify! json IEncodeClojure ...))
(js->clj (fix-json json))
I’ve been excited to finally go through a hello-world exercise using the CLJS webpack guide. I’ve got a question about managing npm dependencies. It might be a dumb one: I’m don’t really know what I’m doing.
My 10-line hello world just uses reagent
, and my deps.edn just uses clojure, clojurescript, and reagent. I’m using a very simple build.edn with :target :bundle
, using webpack, pretty much ripped right out of the guide.
The question: :optimizations :advanced
results in a broken app unless deps.edn excludes [cljsjs/react cljsjs/react-dom]
from reagent
and I install them in local node modules. I’m trying to understand why this is, and what the ramifications are for maintaining a larger project.
Any guidance, even just a LMGTFY with the right search terms, would be much appreciated! I don’t know what I don’t know.
thanks for the specify!
pointer
this is how it looks like now:
(ns buggy-js-clj.core
(:require
[clojure.string :as str]
[goog.object :as gobject]))
(let [json #js {:constructor true}]
(defn fix-json [json]
(specify! json IEncodeClojure
(-js->clj [x {:keys [keywordize-keys] :as options}]
(let [keyfn (if keywordize-keys keyword str)]
(persistent!
(reduce
(fn [r k] (assoc! r (keyfn k) (js->clj (gobject/get x k))))
(transient {})
(remove #(str/starts-with? % "cljs$core$IEncodeClojure$")
(js-keys x))))))))
(print (-> (fix-json json)
(js->clj))))
had to remove keys starting with "cljs$core$IEncodeClojure$"
because specify adds them
{constructor true, cljs$core$IEncodeClojure$ {}, cljs$core$IEncodeClojure$_js__GT_clj$arity$2 #object[Function]}
@filipematossilva (specify! (. json -prototype) ...)
to avoid that issue
prototype of #js {}
is undefined
I thought it was because of #js {:constructor true}
but it's just object literal in general
you could add the prototype then do it
all this can go in a helper fn
I'm having a problem with Clojurescript, spec, and spec.test.alpha. Anytime I use spec/fspec
with spec.test.alpha/instrument
, I'm getting an error:
Var clojure.test.check.properties/for-all* does not exist, clojure.test.check.properties never required
.
Actually putting clojure.test.check.properties
in the ns
require causes the REPL to hang.
I mistakenly made an https://github.com/bhauman/figwheel-main/issues/272 about this before I realized this happens with clj -m cljs.main
. I have more info and a minimal reproduction at that issue if you'd like to take a look.