clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
gekkou 2020-11-20T02:22:48.307Z

Trying to compile cljs into a single js file, but it is failing to pull npm dependencies into the file and eliminate dead code

gekkou 2020-11-20T02:23:33.307600Z

I have an extern.js file that includes function names and have a shadow-cljs.edn but neither of them are bundling everything

thheller 2020-11-20T09:31:18.313Z

you did not provide enough information to help. which command did you run? what is the build config? which :target?

gekkou 2020-11-20T02:24:15.308400Z

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

2020-11-20T02:43:55.309900Z

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.

2020-11-20T02:44:44.310500Z

but I'm not sure what's the most practical way to include that in a build.

greensponge 2020-11-20T08:37:47.312700Z

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

cfleming 2020-11-20T09:17:43.312800Z

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.

👍 1
greensponge 2020-11-20T09:49:20.313300Z

@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!

cfleming 2020-11-20T10:25:53.313500Z

No problem, I’m glad it’s helping!

2020-11-20T15:34:18.316500Z

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.

Filipe Silva 2020-11-20T16:03:17.319Z

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

thheller 2020-11-22T17:55:50.356100Z

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.

thheller 2020-11-22T17:56:21.356300Z

instead you can use something much simpler like https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/json.cljs#L4

👍 1
thheller 2020-11-22T17:59:10.356700Z

cljs.user=> (require '[shadow.json :as j])
nil
cljs.user=> (j/to-clj #js {:constructor true})
{:constructor true}

Filipe Silva 2020-11-20T16:05:13.320900Z

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

Filipe Silva 2020-11-20T16:05:26.321300Z

is the best place to report this sort of thing still the JIRA?

alexmiller 2020-11-20T16:05:44.321600Z

best place to report issues is on https://ask.clojure.org

alexmiller 2020-11-20T16:05:55.322Z

unless you have a jira acct, in which case that's ok too

Filipe Silva 2020-11-20T16:07:55.323Z

I have a JIRA acc actually, might as well open it there in case I manage to submit a patch for it then

Filipe Silva 2020-11-20T16:09:40.323400Z

hm wonder if it's related to https://clojure.atlassian.net/browse/CLJS-3201

p-himik 2020-11-20T16:12:09.323500Z

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.

Filipe Silva 2020-11-20T16:18:41.323800Z

here is the JIRA issue https://clojure.atlassian.net/browse/CLJS-3285

Filipe Silva 2020-11-20T16:19:08.323900Z

could you add that comment in the JIRA issue?

Filipe Silva 2020-11-20T16:19:25.324100Z

sounds like it would be helpful for whoever ends up looking at the issue

p-himik 2020-11-20T16:21:35.324300Z

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. :)

p-himik 2020-11-20T16:21:47.324500Z

The js->clj implementation is very straightforward.

Filipe Silva 2020-11-20T16:31:45.324700Z

I'll add a comment there saying what you said, I found it helpful

👍 1
p-himik 2020-11-20T16:33:15.325Z

To be more concrete: (assert (true? (type #js {:constructor true}))).

Filipe Silva 2020-11-20T16:43:45.325500Z

hmmm it looks like type should be checking it's a function

Filipe Silva 2020-11-20T16:48:30.325700Z

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)))

p-himik 2020-11-20T16:52:38.325900Z

Well, then you should expect that (js->clj #js {:constructor #()}) will still be broken.

Filipe Silva 2020-11-20T16:54:14.326100Z

hm...

Filipe Silva 2020-11-20T16:56:06.326300Z

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

dnolen 2020-11-20T16:57:36.326600Z

hrm I don't know about this

dnolen 2020-11-20T16:57:48.327Z

using constructor is problematic because JavaScript

dnolen 2020-11-20T16:58:05.327400Z

but if you have solution that doesn't create more problems will take a look

dnolen 2020-11-20T16:58:20.327600Z

but my first thought is don't do that

dnolen 2020-11-20T16:59:21.328200Z

I think transit works here because it's data exchange only

Filipe Silva 2020-11-20T17:00:32.329100Z

my concrete situation is that I have data stored in Firebase containing string keys chosen by users

Filipe Silva 2020-11-20T17:00:46.329500Z

when downloading and parsing it, I run into this

dnolen 2020-11-20T17:01:14.330300Z

if you look at js->clj you should see you have options

dnolen 2020-11-20T17:01:19.330600Z

IEncodeClojure is a thing

dnolen 2020-11-20T17:01:29.330900Z

you could extend to that on the fly for your stuff to avoid this

Filipe Silva 2020-11-20T17:02:20.331700Z

I wasn't aware I had that option, it does sound good for my case

Filipe Silva 2020-11-20T17:02:24.331900Z

thanks for bringing it up

dnolen 2020-11-20T17:02:43.332200Z

yeah there needs to be trapdoor for some cases

dnolen 2020-11-20T17:03:47.332900Z

I'm switching that issue to minor for now unless you discover for some reason that approach doesn't work for you

Filipe Silva 2020-11-20T17:03:56.333100Z

SGTM

Filipe Silva 2020-11-20T17:04:17.333600Z

wasn't really sure what the correct priority was, it defaulted to major so left that

dnolen 2020-11-20T17:04:46.334100Z

which is fine, can always adjust priority later

Filipe Silva 2020-11-20T17:37:40.335600Z

@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

Filipe Silva 2020-11-20T17:38:26.336200Z

but I think I can instead convert the JS data itself into map entries, falling into the previous cond

Filipe Silva 2020-11-20T17:38:28.336500Z

(map-entry? x)
                (MapEntry. (thisfn (key x)) (thisfn (val x)) nil)

Filipe Silva 2020-11-20T17:45:03.336800Z

either that or removing that entry and adding it later manually...

dnolen 2020-11-20T18:33:44.337300Z

extend-type true isn't right

dnolen 2020-11-20T18:33:59.337600Z

use (specify! ...)

dnolen 2020-11-20T18:34:06.337800Z

on the value directly

dnolen 2020-11-20T18:34:27.338200Z

specify is probably good enough too?

dnolen 2020-11-20T19:16:06.338900Z

actually you do need specify! because your value isn't ICloneable

dnolen 2020-11-20T19:16:29.339500Z

(defn fix-json [json] (specify! json IEncodeClojure ...))

dnolen 2020-11-20T19:16:59.339800Z

(js->clj (fix-json json))

2020-11-20T19:21:21.340200Z

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.

2020-11-20T19:21:24.340300Z

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.

Filipe Silva 2020-11-20T20:04:08.340600Z

thanks for the specify! pointer

Filipe Silva 2020-11-20T20:04:17.340800Z

this is how it looks like now:

Filipe Silva 2020-11-20T20:04:19.341Z

(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))))

Filipe Silva 2020-11-20T20:04:51.341200Z

had to remove keys starting with "cljs$core$IEncodeClojure$" because specify adds them

Filipe Silva 2020-11-20T20:05:00.341400Z

{constructor true, cljs$core$IEncodeClojure$ {}, cljs$core$IEncodeClojure$_js__GT_clj$arity$2 #object[Function]}

dnolen 2020-11-20T20:11:28.342200Z

@filipematossilva (specify! (. json -prototype) ...) to avoid that issue

Filipe Silva 2020-11-20T20:25:29.342300Z

prototype of #js {} is undefined

Filipe Silva 2020-11-20T20:26:14.342500Z

I thought it was because of #js {:constructor true} but it's just object literal in general

dnolen 2020-11-20T20:43:49.342700Z

you could add the prototype then do it

dnolen 2020-11-20T20:43:53.342900Z

all this can go in a helper fn

neilyio 2020-11-20T23:07:50.345100Z

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.

neilyio 2020-11-20T23:10:52.347200Z

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.