Fork me on GitHub
#clojurescript
<
2018-07-17
>
nha07:07:17

What are the differences between the browser and node: https://clojurescript.org/reference/compiler-options#target If I intend to make a cljs library used by JS code, is there a way to make it work for both (like some NPM packages do)?

urbanslug07:07:51

So I have this that works in the repl

(def m {:id 1 :name "Jane Doe"})
(-> m vals string/join)
"1Jane Doe"
however in the browser. in a reagent component I can't do this
(js/console.log (-> m vals string/join))
TypeError: me.cljs$core$IMapEntry$_val$arity$1 is not a function which implies I am trying to call a map as a function when I take out the string/join I get a weird object along these lines
(js/console.log (-> m vals))
Object { mseq: {…}, _meta: null, "cljs$lang$protocol_mask$partition0$": 32374988, "cljs$lang$protocol_mask$partition1$": 0 }

urbanslug07:07:39

Another thing I have data from a cljs file and when I try to call vals I get an error: (-> map vals)

== JS EXCEPTION ==============================
ENCODING FAILED
==============================================

urbanslug07:07:25

when I copy and paste it into the repl it works

urbanslug07:07:41

so I guess the repl isn't reading the encoding of the data from the file

urbanslug08:07:36

Another thing (-> map vals (partial string/join " ") ) gives me a result such as #object[G__24608] instead of a string joined with spaces

thheller08:07:41

@urbanslug do you have https://github.com/binaryage/cljs-devtools installed? should make it much easier figuring out what objects are in your map

thheller08:07:26

it looks like you just have some unprintable stuff in your map

urbanslug08:07:02

I don't have devtools installed

urbanslug08:07:40

@thheller the problem with encoding is only in the repl and doesn't bother me much because I'm just testing stuff out

thheller08:07:28

the ENCODING FAILED just means that the REPL failed to pr-str the result of you eval

thheller08:07:44

so it can't transmit the encoded version back to your REPL output

urbanslug08:07:38

@thheller I have this in my source file

(def columns [:id :name])
(defrecord data-generator [id name])

(def data
  [(->data-generator 123 "Von Dutch")
   (->data-generator 311 "Joms Viking")])
in the repl
=> data
[{:id 123, :name "Von Dutch"} {:id 311, :name "Joms Viking"}]
=> (-> data vals)
== JS EXCEPTION ==============================
ENCODING FAILED
==============================================

urbanslug08:07:51

> it can't transmit the encoded version back to your REPL output Ah @thheller then that would make sense because whenever I call vals in my component I get weird errors and even when I js/console.log it it gives a weird object

urbanslug08:07:30

plus tbh the result of printing data is weird

urbanslug08:07:03

it's not exactly like that it's [#example.components.views.table.data-generator{:id 123, :name "Von Dutch"} #example.components.views.table.data-generator{:id 311, :name "Joms Viking"}]

thheller08:07:24

why are you calling vals on a vector?

urbanslug08:07:41

My bad. tried again for the map and failed

(-> data first map?)
true
 (-> data first vals)
== JS EXCEPTION ==============================
ENCODING FAILED
==============================================

thheller08:07:10

but its not a map? its a record right?

thheller08:07:19

(vals {:a 1}) works fine

urbanslug08:07:29

it is a record

thheller08:07:49

[1:1]~demo.browser=> (defrecord Foo [a b])
demo.browser/Foo
[1:1]~demo.browser=> (vals (->Foo 1 2))

thheller08:07:55

this fails. looks like a bug in CLJS

urbanslug08:07:18

Jesus christ! I had a feeling it was in the record but thought it couldn't be

urbanslug08:07:43

I was starting to consider calling js functions like so

(def js-obj (-> data first clj->js))
(.values js/Object m)

urbanslug08:07:47

and joining them

urbanslug08:07:24

@thheller a little help there's something weird here.

[1:1]~demo.browser=> (def x (first data))
[1:1]~demo.browser=> (vals (into {} x))
(123 "Von Dutch")


[1:1]~demo.browser=> (-> data first (partial into {}) vals)
nil

urbanslug08:07:35

notice how it fails with threading?

thheller08:07:59

because you are threading into the first arg of partial

urbanslug08:07:45

[1:1]~demo.browser=> (->> data first (partial into {}) vals)
nil
no luck

oscar17:07:30

You didn't want to use partial here. Macroexpanded, it'll look like (vals (partial into {} (first data))) which, when executed, will try to call vals on a function. You want (->> data first (into {}) vals).

urbanslug08:07:56

but let me look at the threading docs

urbanslug08:07:02

I must be missing something

thheller08:07:24

what are you trying to do exactly?

thheller08:07:52

partial creates a function and you can;t call vals on a function

thheller08:07:18

(vals (partial into {} (first (data))))

urbanslug08:07:38

hmmm I'm trying to make the result of first be applied to into {}

urbanslug08:07:56

I'm trying to compose them

thheller08:07:20

(->> data (map #(into {} %)) vals first)

urbanslug08:07:03

I think I'll leave the records alone for now:

[1:1]~demo.browser=>  (->> data (map #(into {} %)) vals)
== JS EXCEPTION ==============================
ENCODING FAILED
==============================================

thheller08:07:21

maybe vals in general is just busted

urbanslug08:07:48

yeah this seems to work

(->> data (map #(into {} %)))
({:id 123, :name "Von Dutch"} {:id 311, :name "Joms Viking"})

urbanslug08:07:00

wonder if I should call js functions for this

thheller08:07:52

what exactly is the problem though? AFAICT the problem only appears when trying to print the thing

thheller08:07:56

why are you printing?

thheller08:07:06

is that the actual problem or something else?

urbanslug08:07:28

I printed because

(->> data (map #(into {} %)) vals first)
nil

urbanslug08:07:45

so I took out the first to see what vals was giving

urbanslug08:07:06

Anyway what I'm trying to do is generate keys for a table

urbanslug08:07:28

I'm using a for loop in a reagent component and want to silence the warning about each element having a unique key

urbanslug08:07:55

I want to take data from the row and use part of it to make a key

urbanslug08:07:28

At this point I'm thinking of letting the warning just stay

thheller08:07:32

don't you just need (:id row) for that?

urbanslug08:07:53

It's a general function that I'm hoping will take different types of data

urbanslug08:07:21

for now I can specify that data passed to it have a unique id

thheller08:07:00

could make a generic {:data data :key-fn :id} and then call (key-fn row) inside

urbanslug08:07:48

are you saying that every data point comes with an id that is a function?

thheller08:07:24

no you pass in a function that extracts the id from the row

thheller08:07:39

{:data :key-fn (fn [row] (get row :id)) is basically the long form

witek09:07:21

Hello. I need a hint for the following problem: When writing hiccup for re-frame, I often have a vector containing mostly static content: [:ul [:li "A"] [:li "B"] [:li "C"]]. But sometimes I have to include values from an other sequence into my list. Here I need to include elements from the vector (def subitems ["B.1" "B.2"]) into my hiccup. Is there an elegant way to place some code between [:li "B"] and [:li "C"] which inserts elements from subitems wrapped in [:li ...]s?

witek09:07:56

I am looking for something which would be used like that: [:ul [:li "A"] [:li "B"] (magic-for [subitem subitems] [:li subitem]) [:li "C"]].

witek09:07:35

In Clojure unquote-splicing would do the trick. But how to do this in ClojureScript?

Roman Liutikov09:07:18

how about (->> (concat [:ul [:li "A"] [:li "B"]] (magic-for ...) [:li "C"]) (into []))

Roman Liutikov09:07:57

but in general it’s better to keep Hiccup static

witek09:07:55

Exactly. I hoped for something which does not obscure my beatiful hiccup.

Roman Liutikov09:07:51

unquote-splicing should work here as well

`[1 2 3 ~@[5 6] 4] ;; [1 2 3 5 6 4]

Roman Liutikov09:07:57

`[:ul
  [:li "A"]
  [:li "B"]
  ~@(for [idx (range 2)]
      [:li idx])
  [:li "C"]]
;; => [:ul [:li "A"] [:li "B"] [:li 0] [:li 1] [:li "C"]]

witek09:07:52

So what does this message mean? -> WARNING: Use of undeclared Var cljs.core/unquote-splicing at line 63

hlolli09:07:39

I wonder if this unquote splicing on for loop, takes its lazyness into account. Never tried this, always use into myself.

hlolli09:07:20

ah, I also guess you'd need this in a macro in a .clj namespace

chrisblom09:07:19

unquote-splicing realizes the entire sequence, no issues with lazyness

Roman Liutikov10:07:34

@witek hmm, perhaps it doesn’t work when compiled since cljs doesn’t include the whole compiler/reader into output JS

Roman Liutikov10:07:29

just tried it with :none optimizations, works fine

benzn10:07:08

I think I've hit a strange issue with ClojureScript self hosted REPLs and I can't tell if it's a bug or if I'm misunderstanding: This works (on http://clojurescript.io):

cljs.user=> (ns macros.core$macros) (defmacro hello [x] (prn &form) `(inc ~x))
true
macros.core$macros=> (macros.core/hello 1)
2
This does not (on a fresh page load)
cljs.user=> (do (ns macros.core$macros) (defmacro hello [x] (prn &form) `(inc ~x)))
true
macros.core$macros=> (macros.core/hello 1)
ERROR - undefined is not an object (evaluating 'macros.core.hello')
The only difference here is that I've wrapped everything in a do form in the second example. Curiously, if I repeat said form... it works!
cljs.user=> (do (ns macros.core$macros) (defmacro hello [x] (prn &form) `(inc ~x)))
true
macros.core$macros=> (do (ns macros.core$macros) (defmacro hello [x] (prn &form) `(inc ~x)))
true
macros.core$macros=> (macros.core/hello 1)
2
I thought perhaps do was lazily computing something, but documentation doesn't seem to indicate this is the case. Curiously... after the first do form, macros.core is {} rather than the expected {hello: [Function]} Perhaps do is introducing an additional (JS) scope that is mucking up how namespaces work in ClojureScript?

benzn11:07:48

Going down a layer, good code:

goog.provide("macros.core$macros");
(function (){
(function (){
macros.core$macros.hello = (function macros$core$macros$hello(_AMPERSAND_form,_AMPERSAND_env,x){
cljs.core.prn.call(null,_AMPERSAND_form);

return cljs.core.sequence.call(null,cljs.core.concat.call(null,cljs.core._conj.call(null,cljs.core.List.EMPTY,new cljs.core.Symbol("cljs.core","inc","cljs.core/inc",(-879172610),null)),cljs.core._conj.call(null,cljs.core.List.EMPTY,x)));
}); return (
new cljs.core.Var(function(){return macros.core$macros.hello;},new cljs.core.Symbol("macros.core$macros","hello","macros.core$macros/hello",(-113529239),null),cljs.core.PersistentHashMap.fromArrays([new cljs.core.Keyword(null,"ns","ns",(441598760)),new cljs.core.Keyword(null,"name","name",(1843675177)),new cljs.core.Keyword(null,"file","file",(-1269645878)),new cljs.core.Keyword(null,"end-column","end-column",(1425389514)),new cljs.core.Keyword(null,"column","column",(2078222095)),new cljs.core.Keyword(null,"line","line",(212345235)),new cljs.core.Keyword(null,"macro","macro",(-867863404)),new cljs.core.Keyword(null,"end-line","end-line",(1837326455)),new cljs.core.Keyword(null,"arglists","arglists",(1661989754)),new cljs.core.Keyword(null,"doc","doc",(1913296891)),new cljs.core.Keyword(null,"test","test",(577538877))],[new cljs.core.Symbol(null,"macros.core$macros","macros.core$macros",(1865249614),null),new cljs.core.Symbol(null,"hello","hello",(1395506130),null),null,(40),(25),(1),true,(1),cljs.core.list(new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Symbol(null,"x","x",(-555367584),null)], null)),null,(cljs.core.truth_(macros.core$macros.hello)?macros.core$macros.hello.cljs$lang$test:null)])));})()
;

return macros.core$macros.hello.cljs$lang$macro = true;
})()
Bad code
(function (){
goog.provide('macros.core$macros');
goog.require('cljs.core');

(function (){
cljs.user.hello = (function cljs$user$hello(_AMPERSAND_form,_AMPERSAND_env,x){
cljs.core.prn.call(null,_AMPERSAND_form);

return cljs.core.sequence.call(null,cljs.core.concat.call(null,cljs.core._conj.call(null,cljs.core.List.EMPTY,new cljs.core.Symbol("cljs.core","inc","cljs.core/inc",(-879172610),null)),cljs.core._conj.call(null,cljs.core.List.EMPTY,x)));
}); return (
new cljs.core.Var(function(){return cljs.user.hello;},new cljs.core.Symbol("cljs.user","hello","cljs.user/hello",(-1955175765),null),cljs.core.PersistentHashMap.fromArrays([new cljs.core.Keyword(null,"ns","ns",(441598760)),new cljs.core.Keyword(null,"name","name",(1843675177)),new cljs.core.Keyword(null,"file","file",(-1269645878)),new cljs.core.Keyword(null,"end-column","end-column",(1425389514)),new cljs.core.Keyword(null,"column","column",(2078222095)),new cljs.core.Keyword(null,"line","line",(212345235)),new cljs.core.Keyword(null,"macro","macro",(-867863404)),new cljs.core.Keyword(null,"end-line","end-line",(1837326455)),new cljs.core.Keyword(null,"arglists","arglists",(1661989754)),new cljs.core.Keyword(null,"doc","doc",(1913296891)),new cljs.core.Keyword(null,"test","test",(577538877))],[new cljs.core.Symbol(null,"cljs.user","cljs.user",(877795071),null),new cljs.core.Symbol(null,"hello","hello",(1395506130),null),null,(44),(29),(1),true,(1),cljs.core.list(new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Symbol(null,"x","x",(-555367584),null)], null)),null,(cljs.core.truth_(cljs.user.hello)?cljs.user.hello.cljs$lang$test:null)])));})()
;

return cljs.user.hello.cljs$lang$macro = true;
})()
You'll note that the bad code directly assigns to cljs.user.hello. What?

benzn10:07:37

Hmm, so the ClojureScript compiler produces different JS for the first do and the second do

benzn10:07:44

the second do looks like the line without the do at all

domingues10:07:16

Hi ! Does anyone could advise me for some open source project that i can cooperate and improve my skills in Clojure(script) ?

benzn11:07:43

(FWIW, I tried the equivalent in straight Clojure and the behavior is the same with and without the do)

urbanslug11:07:06

here's a dumb question: What's the purpose of cljs-devtools https://github.com/binaryage/cljs-devtools? To get better errors?

manutter5111:07:55

I’d say “more cljs-friendly debugging output” — it lets you display cljs data structures you can inspect instead of just displaying #object[my.ns.Baseclass]. If you run cljs without the devtools and you do something like (prn {:foo 1 :bar 2}), and then do the same with devtools (and with custom formatters enabled) you’ll see the difference.

urbanslug11:07:47

OK thanks thing is I was using firefox and earlier today someone mentioned it would help with my errors. Also on chromium react devtools are able to notice that I'm using react

Josh Horwitz13:07:48

What are the best recommended resources to give to a team who is new to clojurescript?

bhauman14:07:52

@joshua.d.horwitz what are you looking for? more specifically

Josh Horwitz14:07:02

So basically I have a JavaScript/Node/React team, with no Clojurescript experience. When introduction say React, I had a few books I could give them and some training sites, looking at how to do that with Clojurescript/reframe

bhauman14:07:02

so http://purelyfunctional.tv has a bunch of resources for front-end work

bhauman14:07:08

Lambda Island has a bunch of good stuff as well

bhauman14:07:49

@joshua.d.horwitz these are high quality recent resources

Josh Horwitz14:07:35

Great, thank you so much

bhauman14:07:12

I haven't read it, but it may provide some comfort 🙂

bhauman14:07:16

I'm currently working on documentation for figwheel-main and the tutorial http://rigsomelight.com/figwheel-main/tutorial is fairly extensive

bhauman14:07:53

and I'll throw in this last link

bhauman14:07:12

@joshua.d.horwitz that should be enough to get you started 🙂

Josh Horwitz14:07:55

That's perfect! Thank you so much

bhauman14:07:36

@joshua.d.horwitz oh I didn't mention this because its such a well known Clojure resource https://www.braveclojure.com/clojure-for-the-brave-and-true/

bhauman14:07:56

looks like I'm going to be pinging you for the rest of the day 😉

Josh Horwitz14:07:25

No problem with that, I love the attention!

🙂 4
mfikes14:07:57

If you'd like to try out a pre-release of a new Graal.js REPL environment for ClojureScript, instructions here https://gist.github.com/mfikes/e0dcb57f2d87afc905829f86111fb610 Here is using it to calculate the sum from 1 to 100 with five languages in one REPL. 🙂

cljs.user=> (reduce + (range 1 101))
5050
cljs.user=> (.eval js/Polyglot "R" "sum(1:100)")
5050
cljs.user=> (.eval js/Polyglot "ruby" "(1..100).reduce(:+)")
5050
cljs.user=> (.eval js/Polyglot "python" "sum(x for x in range(1, 101))")
Error: Operation is not allowed for: /private/tmp/graaljs
cljs.user=> (.eval js/Polyglot "python" "sum(x for x in range(1, 101))")
5050
cljs.user=> (.eval js/Polyglot "js" "(100 * (100 + 1)) / 2")
5050

🙂 4
urbanslug15:07:23

Hello, when it comes to routing I'm using secretary and accountant. I'm not using fragments for my routes. What I've noticed is on gitlab pages my routes fail, it tries to load another page and gets a 404/

urbanslug15:07:33

but locally routing works

urbanslug15:07:09

and it seems I ave to use the # for routing in SPAs but is this really not because of gitlab pages?

urbanslug15:07:22

I've not tried deploying on a different env

lilactown16:07:03

@urbanslug you'll need to use # routing, yes

lilactown16:07:12

in order to use non-# routing for SPAs you need to configure your webserver in a certain way. github pages does not allow you to do that configuration

samueldev16:07:45

* or use the history api, right? thats purely client-side and removes the # i thought.

urbanslug16:07:42

I'm willing to do this. So if I use the history API this could work? > or use the history api, right? thats purely client-side and removes the # i thought.

lilactown16:07:41

is it possible to embed a CLJS REPL in a Node.js app?

lilactown16:07:41

I guess I should say, I have a CLJS app that runs on Node.js - is it possible for me to get a REPL in it once it's deployed?

mfikes16:07:30

@lilactown You could consider running things in a self-hosted compatible way

mfikes16:07:51

In short, you would have to forgo :advanced if you are using it.

mfikes16:07:07

This project is probably the easiest path to success for doing something like that https://github.com/Lambda-X/replumb

lilactown16:07:04

hmm. okay, thanks

lilactown16:07:02

I'm trying to get some performance info so I'm hesitant to switch to self hosting 😭

henryw37416:07:33

hey everyone, I'm trying to write a data_readers.cljc file so that different output is produced if a reader fn is being run when reading clojurescript than when reading clojure. I know I could not use data_readers.cljc file and manually set! data-readers but would like to do it via the file if possible. using reader conditionals won't work afaik because in the case of non-self-hosted cljs the code that's running is actually clojure. any ideas please let me know

justinlee16:07:54

@urbanslug the issue is that if you reload a non-fragment route or just cut-and-paste it into a fresh browser window, the browser will try to go fetch that page. the typical fix for this is to redirect everything to index.html so that it just loads up your spa and then the router will load the right page based on the url. gitlab pages doesn’t have support for that, apparently https://gitlab.com/gitlab-org/gitlab-pages/issues/23

urbanslug16:07:52

@lee.justin.m a freaking life saver you are!

urbanslug16:07:16

have a beer on me I'll pay you back when we meet 😛

🍻 4
justinlee16:07:52

@witek @roman01la if you are writing hiccup for re-frame/reagent, I don’t see any real advantage trying to keep hiccup static. arguably one of the nicest (and confusing) aspects of reagent is that it adds one more layer of indirection over react, which enables you to create hiccup structures dynamically using all the normal collection functions (like into). there isn’t really any such thing as “static” hiccup in reagent, unless i misunderstand what you guys mean

witek19:07:13

@justinlee Static hiccup with ~@(...) at some places is much more readable then a threading macro and into. When reading my view code I need to see HTML concepts immediately. Not how it is constructed. When prettyfying my UI I need to move divs, wrap and unwrap them with other divs and so on. This is much easier when there are just hiccup vectors not mixed with collections concepts.

oscar21:07:32

You shouldn't need to deal with quoting and splicing. Just leave stuff as non-vector seqs.

justinlee22:07:34

does that work in reagent? i’ve never tried.

justinlee22:07:24

interesting apparently it does work, though you have to set a key on it

justinlee22:07:58

yea so @witek the magic-for you are looking for is really just regular old for:

(defn test-component
  []
  [:div [:div "a"] 
   (for [x (range 1 10)] ^{:key x} [:div x]) 
   [:div "b"]])
You just need to set a key to avoid the warning. You could write a for-wrapper that just sets the key equal to the index to shorten it up.

oscar22:07:21

Why are you using metadata for :key? You can pass it as a prop.

justinlee22:07:10

you can with built-ins but not generally

justinlee22:07:25

(in reagent)

Marcin21:07:21

Hey, what do you people use for the most robust solution for routing in your SPA? Something that will keep the animations alive (best probably would be without switching components). Cheers!

benzn22:07:21

Can anyone help with the magic incantation of the ClojureScript real to give me verbose output?

benzn22:07:02

(require '[cljs.repl :as repl])
(require '[cljs.repl.node :as node]) 
(def env (node/repl-env :repl-verbose true)) 
(repl/repl env)

benzn22:07:08

This doesn't seem to work

justinlee22:07:41

@marcinszyszko I don’t know about animations. I have used secretary + accountant and it is simple and serviceable. I suppose you could use interop to use react router, which will do everything, obviously.

Marcin08:07:23

Thanks @lee.justin.m. Secretary works as a charm here, but the project is no longer maintained.

bhauman22:07:42

my pleasure 🙂

ericnormand22:07:27

@josh_horwitz do look at this page: https://purelyfunctional.tv/framework/re-frame/ There's plenty of free guides and there's a couple of paid video courses.

john23:07:38

I'm trying to test out node's new worker_threads module: https://nodejs.org/api/worker_threads.html

john23:07:31

Using the new cljs test-runner, (nodejs/require "worker_threads"), I constantly get Error: Cannot find module 'worker_threads'

john23:07:55

But requiring "fs" works

john23:07:56

hmm, actually, maybe my flag insertion isn't actually working...

mfikes23:07:10

@john If it helps, I had made a similar revision that adds extra args to node https://github.com/mfikes/clojurescript/commit/3e13026898acaa0f89eaa6eaa3be9411e084cf2d

tomjkidd23:07:20

I am trying to use om.next to write a custom shouldComponentUpdate override. If I need to access the clojure-friendly state value in my component code, is using goog.object/get in this fashion https://github.com/omcljs/om/blob/master/src/main/om/next.cljc#L203-L205 the way you are supposed to access state (without the macro parts)?

tomjkidd23:07:10

I’m used to using the api methods like om.next/get-state for cases like defining the render function, but after looking through the docs couldn’t locate guidance for this situation.

tomjkidd00:07:18

I decided to ignore the next-state argument, and use om/get-rendered-state to get the current state, and om/get-state to get the next state.

john23:07:38

@mfikes Thanks! I'll use that.

mfikes23:07:26

Here is how it is used @john

clj -m cljs.main -ro '{:node-args ["--experimental-worker"]}' -re node -r

john23:07:48

understood

john23:07:29

It's working! kinda

john23:07:44

stdout doesn't work on node workers 😕

john23:07:29

SharedArrayBuffers are working