Fork me on GitHub
#clojurescript
<
2018-06-27
>
Yehonathan Sharvit05:06:46

Is it possible to use javascript async and await in Clojurescript?

justinlee05:06:51

@viebel short answer is no. long answer is https://clojureverse.org/t/async-generator-functions-in-cljs-requesting-feedback/2262 pragmatic answer is check out the promesa library which has some solutions (see their alet and async macros)

Yehonathan Sharvit05:06:06

thanks @lee.justin.m. My use case is that I want to expose a clojurescript function that returns core.async channel for javascript consumption

justinlee05:06:25

I do not know if there is any reasonable way to directly consume a channel from javascript. I think you’d want to expose a more idiomatic interface, like an event-based model for streams or a promise model for a single value

Yehonathan Sharvit05:06:11

For the moment, I am handling it with javascript promises like that:

(defn foo-js-promise [args]
   (js/Promise. (fn [resolve reject]
                 (go (let [[status data] (<! (foo args))]
                          (if (= :ok status)
                            (resolve data)
                            (reject data)))))))

justinlee05:06:55

yea if there’s just one value, then that’s a pretty good interface. any reason why you don’t like it?

justinlee05:06:20

(note, you can consume that using async/await syntax from javascript)

justinlee05:06:01

async function f() { let x = await foo-js-promise(args); … }

justinlee05:06:18

await is just sugar for waiting on a promise

Yehonathan Sharvit05:06:40

thanks @lee.justin.m this was exactly what I was looking for

👍 4
deg13:06:29

How do I trigger advanced optimization on js files included in my cljs project? One of my projects starts up slowly, and I finally took a look at it today. It is loading a 1.5MB app.js, of which about half is a complete copy of semantic-ui-react.inc.js indirectly loaded frm cljsjs from one of my project dependencies. I'm compiling with :optimizations :advanced, so I'm surprised that it was not optimized. Is this expected behavior? Or did I just configure something wrong?

thheller13:06:12

@deg forein libs (ie. cljsjs) are never passed through :advanced so yes that is totally expected.

deg13:06:44

Ah, ok. Too bad. Is there any way to force it to be optimized, or any other recommended tricks/techniques?

dnolen16:06:32

@deg what is the gzipped size? That’s what really matters

dnolen16:06:17

And yes there’s nothing magic to be done above massive non Closure libs

justinlee16:06:03

@deg i haven’t looked at semantic-ui specifically but most of these ui libraries are huge because they provide 100 different components and you only use 10 of them. all the ones I’ve looked at solve this by making it possible to include each component separately.

alandipert18:06:14

do i remember right that cljs supported "compiler macros" at one point? the ability to define macros to use in place of inline function calls. does it still?

anton18:06:01

Has anyone tried to use Clojurescript with the existing NPM ecosystem?

anton18:06:03

I found the announcement about adding npm-deps (https://clojurescript.org/guides/javascript-modules) but compiling with a react-router-dom dependency leads to java.lang.NullPointerException

justinlee18:06:56

the :npm-deps feature is a bit advanced and doesn’t have all the kinks worked out yet

anton18:06:50

oh, so Boost isn't really intended for projects that need JS-community integration?

anton18:06:41

which is the more common between those two options? It seems like the first one could be integrated with Boot

justinlee18:06:30

oops sorry @anton i misread. you are actually using the :foreign-libs interface, which should work

justinlee18:06:50

i’m not following your question on boot

anton18:06:13

I really like the hot reloading capabilities that a setup with Boot provides, so I'm trying to figure out if it's possible to use Boot in conjunction with either of those two options

justinlee18:06:25

yes it definitely is. if you use the standard cljs compiler (as opposed to shadow-cljs), you can include npm modules using the :foreign-libs interface. the “webpack” solution just makes this a bit simpler by ensuring that all of the dependencies get processed into single browser compatible bundle. this means you just need a single import. boot is really neither here nor there on this: you need to set up webpack outside of boot, produce your bundle, and then pass the right options to the compiler.

justinlee18:06:48

the tricky part here is that some cljs packages (like reagent) already include the javascript they need (like react)

anton18:06:05

right, how do you get around that?

anton18:06:16

when I tried to install react-router without react it didn't work

justinlee18:06:42

you can exclude the cljsjs package like this [reagent "0.7.0" :exclusions [cljsjs/react]] (or whatever it is that you need to exclude) and then add it to the webpack bundle (or whatever solution you are using)

mfikes18:06:12

@alandipert Well, in ClojureScript you can have a macro with the same name as a function and then get the best of both worlds.

alandipert18:06:56

@mfikes thanks, that's exactly what i was looking for

anton18:06:01

I've got one more question - I posted about this on the forum, but maybe you could give some more in-depth help

anton18:06:12

essentially - is there any way to automatically name namespaces?

anton18:06:29

often react projects have really nested folder hierarchies and having to write out 10 levels of folders seems really awkward

justinlee18:06:56

so i’m not an expert, but I think the answer to your question is no. and i’m pretty sure it’s due to the underlying java infrastructure (but I don’t really know). maybe there is some solution. let’s see if anyone answers. (maybe symlinks?)

dnolen19:06:00

@anton I’ve never worked on a project with ten levels nested, seems gratuitous :)

dnolen19:06:45

3 or 4 seems common but not much more, which isn’t that tedious

anton19:06:13

maybe that was a bit of an overexaggeration

anton19:06:30

but that makes sense, it will just take a little getting used to

anton19:06:48

especially after being able to use index.js to just import a bunch of modules from a folder

anton19:06:02

I assume you can't do the following:

anton19:06:09

import * as scenes from './scenes'

lilactown19:06:13

you can do something similar

lilactown19:06:42

e.g. if you have foo.bar.baz , , foo.bar.abc, etc. you can have a top-level foo.bar that re-exports the public vars in those lower namespaces

lilactown19:06:46

not sure how this might impact Closure's dead-code elimination tho

anton19:06:57

what file would that be in? or does the filename not matter?

lilactown19:06:45

if you have foo/bar/baz.cljs, foo/bar/xyz.cljs, foo/bar/abc.cljs, you can also have a foo/bar.cljs

lilactown19:06:40

the only bummer is that ClojureScript doesn't have use so you'll have to manually write out all of the vars you want to expose in foo/bar.cljs

anton19:06:16

couldn't you do :require ["foo.bar" :as name]?

lilactown19:06:39

since it's a CLJS namespace you would use :require [foo.bar :as name]

lilactown19:06:27

but what I'm talking about is inside of foo/bar.cljs you'll need to write something like:

(ns foo.bar
  (:require [foo.bar.baz :as baz]
            [ :as xyz]
            [foo.bar.abc :as abc]))

;; export vars from baz
(defn do-thing [x]
  (baz/do-thing x))

(defn something-else [x y]
  (baz/something-else x y))

;; ...

anton19:06:18

I imagine that you could write some sort of macro

anton19:06:23

but even then it would still be painful

anton19:06:01

honestly I'm starting to realize that this problem isn't big enough to warrant an elaborate solution like that

grav19:06:03

Could you maybe have a registry of components (which in Reagent are more or less just data) and then a lookup function for that registry?

grav19:06:24

Your registry would be a data structure with some kind of hierarchy that replaces the namespace hierarchy

anton19:06:51

and then you could define a defcomponent macro that automatically places functions in that registry...

grav19:06:42

It could be a macro, but it could also just start out as a function 🙂

anton19:06:14

why so? (I'm still learning when to use macros vs functions)

grav19:06:08

I guess it’s a question of temper. There are probably performance advantages to using components, but there’s a certain simplicity to using functions.

anton19:06:16

but the macros would still return functions

anton19:06:33

they would just have an extra step that adds the functions to a global map

grav19:06:13

Yes, it would probably make it easier to use. A macro can help defining an API. But often I’m a bit reluctant on settling on a certain API, so I guess I tend to start out with functions that manipulate data, which may be more tedious to use, but also very explicit.

anton19:06:17

That definitely makes sense - you start out with functions and once you have a working copy migrate to macros.

anton20:06:01

does anyone know what this operator does: :>

lilactown20:06:28

that's a reagent specific thing for interoping with vanilla React components

anton20:06:44

do you need to import it?

lilactown20:06:52

(I'm guessing you're using Reagent and it's in something like [:> SomeComponent ...])

justinlee20:06:16

you don’t need to import it. it is built into the parser

anton20:06:16

awesome! thanks

anton20:06:30

@lee.justin.m your solution to use Shadow-CLJS also worked perfectly

anton20:06:51

the config file is also so small and neat compared to lein's

justinlee20:06:55

great! i think that’s the smoothest path.

jmromrell21:06:55

I think I have a fundamental misunderstanding of secretary routing. With no prefix set, and the following routes:

(secretary/defroute "/b" [query-params]
  (reset! current-render-fn (fn [] [:h1 "B"])))

(secretary/defroute "/b/x" [query-params]
  (reset! current-render-fn (fn [] [:h1 "B/X"])))

(secretary/defroute "/b/*" [query-params]
  (reset! current-render-fn (fn [] [:h1 "B CATCH ALL"])))

(secretary/defroute "*" [query-params]
  (reset! current-render-fn (fn [] [:h1 "CATCH ALL"])))
Why do I see "B/X" when I visit http://localhost:3000/a/#b/x ? Similarly, why do I see "CATCH ALL" when I visit http://localhost:3000/b/x ?

jmromrell21:06:45

(the server is routing everything to the same HTML fragment with the same compiled cljs)

jmromrell21:06:24

Nvm, it had to do with a misunderstanding of the values I was dispatching on. Tokens from HistoryEventType/NAVIGATE events only provide the url fragment found after the #, instead of the entire path