Fork me on GitHub
#clojurescript
<
2018-03-29
>
zaphodious02:03:40

Hello all! I'm working on a library to help with working on Service Workers. Would anyone be willing to take a look and give some advise/contribute?

john13:03:24

This is cool! I'll definitely be checking it out. Thanks.

zaphodious02:03:44

I'm relatively inexperienced, and as a result there's quite a bit I don't know about web programming. There doesn't seem to be any cljs library that deals with the service worker api directly, though, so I might as well get the ball rolling.

zaphodious02:03:41

That being said, I know that I require assistance with this. I want it to be done correctly, and while I can write the code I know that I'm not fully equipped to make the correct decision every time.

jeaye02:03:29

Is there any <!! equivalent for ClojureScript's core.async?

jeaye02:03:55

Or, to avoid the XY problem, is there a good way for me to deftest a go block?

zaphodious02:03:18

The test/async macro is what you're looking for

zaphodious02:03:07

though that's only CLJS, so any cljc tests will require a reader-conditional switch somewhere.

jeaye02:03:23

CLJS is all I need, in this case.

jeaye02:03:09

Ah, interesting. I wonder why a search for async didn't turn up anything in the test.check repo.

jeaye02:03:29

This is part of cljs.test, not test.check. That'd be why.

jeaye02:03:11

async function main() {
  await client.init();

  await client
    .timeoutsImplicitWait(120 * 1000)
    .waitForVisible("~email");

  await client.element("~email").setValue("");
  await client.element("~passphrase").setValue("secret")
  await client.click("~sign-in");

  await client
    .pause(10 * 1000)
    .end();
};

👍 4
jeaye02:03:44

Assuming I've a JS lib I'm consuming which behaves like this, how can I manage the analogous await in ClojureScript?

jeaye02:03:15

I've tried using https://github.com/jamesmacaulay/cljs-promises in a go block, but what's returned apparently isn't usable with cljs-promises. When I log it, I just see { state: 'pending' }.

zaphodious02:03:57

Total stab in the dark - js interop with promises, and use 'then' chaining to simulate await?

jeaye02:03:35

Yeah, I know of that option. It'll work.

jeaye02:03:20

I'm hoping there's something which will not follow that pattern, but instead the async/`await` pattern.

jeaye03:03:14

@achythlook That looks great! I'll give it a shot.

Desmond04:03:41

I want to re-use a bit of code (validation) on the client (cljs) and the server (clj). What is the easiest way to do this? npm package and a jar? Its really only about 50 lines but I don't want multiple copies of the source code.

Desmond04:03:47

very cool. thank you.

zaphodious04:03:07

Does anyone know if cljs-http works within service workers?

justinlee05:03:46

@achythlook I haven’t tried but I’m pretty sure the answer is no. cljs-http is based on xhrio, which is based on xhr, and that’s not available in a service worker. you need to look for a library that uses fetch (and i’m not sure that i’ve seen one)

zaphodious05:03:06

Oh bother, that's what I was afraid of

zaphodious05:03:26

Full steam ahead on my wrapper, then 😢

justinlee05:03:51

@achythlook this is probably more than you want to take on, but it looks like google closure library has a shim that makes fetch look like xhr. https://github.com/google/closure-library/blob/43a95c5f7020ef795bc37108298032f4df603d52/closure/goog/net/fetchxmlhttpfactory.js

justinlee05:03:19

you could probably make existing libraries work with a minimal patch if you could figure out what magic option to patch to goog.xhrio

zentrope05:03:02

Or just use fetch as is?

justinlee05:03:15

that’s probably easier 🙂

zentrope05:03:46

Yeah, that’s what I do. Dependencies begone! :)

justinlee05:03:54

if you want the ergonomics of go blocks, json parsing, multipart handling, all that’s already taken care of in cljs-http

zentrope05:03:30

Yeah. I usually just need simple posts and gets.

zentrope05:03:50

Pass in a channel, get the result back that way — that kind of thing, depending on the style of the app.

zaphodious05:03:26

I'm writing a library to help cljs devs write service workers. I'm somewhat unsure of which option would be better - a wrapper over fetch that returns a chan, or being able to use existent libraries.

zentrope05:03:20

I tend to think using channels in a library should be optional.

zentrope05:03:53

Not super familiar with ServiceWorkers, but could you actually even use them at all?

justinlee05:03:01

you have to think about errors very carefully with channels

zentrope05:03:06

Doesn’t Web Workers require serialized data?

zentrope05:03:59

Hm. I got the idea that ServiceWorker was an extension of Web Worker. Might be wrong.

zaphodious05:03:59

Service Workers aren't usually used like web workers. Sure, they have the message-passing capacity, but their main purpose is to be a proxy service (basically) rerouting requests in-browser

zaphodious06:03:09

the idea is that by programatically determining what gets returned from the cache, when, etc, the offline experience for users will be much better

zentrope06:03:57

“Communication with main tread is performed via postMessage API.”

justinlee06:03:27

i hesitate to weigh in too much without thinking, but given that service workers and fetch will always be supported if either is supported, fetch is probably the idiomatic way to build it. as @zentrope points out, channels can come later if that’s what you want

zentrope06:03:29

That’s my question: does the postMessage api require the message to be pure data? If that’s the case, channels won’t work. There’s no sharing between the worker and the main thread, right?

justinlee06:03:56

using a shim that make fetch look like xhr is probably a weird thing to do

justinlee06:03:00

channels could work in the sense that you could provide a channel interface on top of fetch, just like cljs-http provides a channel interface on top of xhrio

zentrope06:03:27

Ah, all within the ServiceWorker context.

justinlee06:03:38

no, all within the main thread context

justinlee06:03:53

that’s the consumer of a SW library

zentrope06:03:26

So there’s no communication between the main thread and the SW?

zaphodious06:03:44

Service workers only communicate with the main thread if the main thread makes an HTTP request. The service worker can then intercept the response, doing whatever it likes. I'm trying to figure out, though I think we just decided, how to enable the service worker to pull in whatever resources it wants.

zaphodious06:03:10

to the main thread, the intercepted request looks identical to a regular request

zaphodious06:03:14

er, response

zentrope06:03:56

If I get you correctly, channels wouldn’t be part of your library.

zentrope06:03:41

A user of it might have a channel with messages that eventually lead to making an HTTP request, but that’s transparent, as you say.

zaphodious06:03:17

Given that fetch and response utilize promises, how should I expose the resultant data to the user? Just give them the promise?

zaphodious06:03:40

Frankly, I find js promises to be a bit more complex then channels. :S

justinlee06:03:56

I think eventually you would want to emulate the major libraries like cljs-http and cljs-ajax so that the network request looks the same to cljs users

zentrope06:03:59

.then(data -> callback(data)).

zentrope06:03:12

.then(data -> (.postMessage data))

justinlee06:03:12

but first you need to do something low level

justinlee06:03:53

what language is that

zaphodious06:03:04

sudojavascript?

zentrope06:03:09

Heh. Have es6 and cljs. ;)

justinlee06:03:28

that’s the most brain hurty thing i’ve seen all day

zentrope06:03:50

(-> (js/fetch url config)
     (.then #(.json %))
     (.then #(.postMessage %)))

zaphodious06:03:21

I wrote a bit of that yesterday. Tried to put ES6 into a cljs repl as the argument to a cljs function. It whined at me.

zentrope06:03:28

Something like that, with appropriate extra transforms and error catching.

justinlee06:03:33

start with that. then take a look at cljs-ajax, which is easier because it’s a callback based interface

zaphodious06:03:48

I suppose I should stop worrying and love the interop

zentrope06:03:12

Config is something like (clj->js {:method "POST" :headers {"content-type" "app/yada"} :body "...") whatever. ;)

zentrope06:03:49

Maybe adopt the “writers” way of working? Use the interop as a rough draft, then see where you can clarify things as you go.

zaphodious06:03:28

That makes sense. A bit better then what I've been doing lol

zentrope06:03:57

I have an app that just uses POST to a /message route, so I just need about 10 lines of interop rather than a ton of deps. If it breaks, I’ll amend.

zaphodious06:03:38

for the record, this is the (slightly out of date) result of my work so far. Gonna go in with a machette in the morning 😄 https://github.com/Zaphodious/hireling/blob/master/src/hireling/core.cljs

zaphodious06:03:38

I'm learning quite a bit, doing this

dum3ng06:03:21

The sastifies? and implements? in the clojurescript 1.10.238 seems not work.

(defprotocol IMe
  (call-me [this]))

(defrecord Person [name]
  IMe
  (call-me [person] (print "please call me " name)))
I wrote the above simple code, and (sastifies? IMe person) and (implements? IMe person) return true in 1.9.946, but return false in 1.10.238.

zaphodious06:03:32

They work fine for me. Are you constructing new instances of Person after reloading the file in the repl?

dum3ng06:03:05

I use figwheel

zaphodious06:03:35

It might be a problem further up the chain. I'm not using figwheel (reload button all the way), and satasfies? correctly identifies a core.async/chan by its protocol in my tests.

zaphodious06:03:49

Try moving the protocol declaration to another namespace? Alternatively, try "Empty Cache and Hard Reload" at the browser (right-click the reload button)

dum3ng06:03:50

I encounter this problem when using om.next. The (om/reconciler? a-reconciler) return false...

dum3ng06:03:58

in 1.10.238

dum3ng06:03:21

It worked fine in previous version.

zaphodious06:03:10

Try deploying your code and see if it still breaks. Regardless, try posting to the google group? Seems that this is an issue worthy of discussion there.

zaphodious06:03:22

er, compiling for deployment

justinlee06:03:04

@dumeng81 i saw this in #om “1.0.0-beta3 out, with fixes for ClojureScript 1.10”

dum3ng06:03:56

@achythlook I will try the new om version

😀 4
dum3ng06:03:29

@lee.justin.m Ok I will have a try.

danielo51510:03:45

What's the point about using promises on cljs ? It's like JS programming but with a different syntax

the-kenny10:03:27

Likely interop?

danielo51511:03:14

Yes, using interop

pesterhazy11:03:33

Promises work well with CLJS. What's the point? I don't understand the question — same reasons as for using promises in JS.

danielo51511:03:21

Because I want to get rid of js while doing cljs. I don't want to program js specific stuff, what's the point then?

miikka11:03:15

Is this expected?

ClojureScript 1.10.238
cljs.user=> (set! *warn-on-infer* true)
repl:1
(function (){try{return cljs.core.pr_str((function (){var ret__6387__auto__ = ;
                                                                              ^

SyntaxError: Unexpected token ;
    at new Script (vm.js:51:7)
    at createScript (vm.js:136:10)
    at Object.runInThisContext (vm.js:197:10)
    at Domain.<anonymous> ([stdin]:76:38)
    at Domain.run (domain.js:322:14)
    at Socket.<anonymous> ([stdin]:73:29)
    at Socket.emit (events.js:180:13)
    at Socket.emit (domain.js:421:20)
    at addChunk (_stream_readable.js:269:12)
    at readableAddChunk (_stream_readable.js:252:13)

danielo51511:03:21

Because I want to get rid of js while doing cljs. I don't want to program js specific stuff, what's the point then?

darwin11:03:27

You can convert promises to core.async channels and vice-versa. Or alternatively use your favourite async library in cljs for that.

danielo51511:03:04

Manually? With a library or with some built in function? Please tell me is the last one

darwin11:03:44

Well, manually. I usually tend to write lightweight glue code to ease interop. Instead of usign cljs interop directly I write simple cljs functions to handle it. And then use those wrappers from cljs code. That feels pretty natural. The technique is described here: https://github.com/binaryage/cljs-oops#side-stepping-the-whole-externs-mess Please note that it does not require cljs-oops. You can use raw cljs interop as well.

danielo51511:03:04

Manually? With a library or with some built in function? Please tell me is the last one

darwin11:03:44

Well, manually. I usually tend to write lightweight glue code to ease interop. Instead of usign cljs interop directly I write simple cljs functions to handle it. And then use those wrappers from cljs code. That feels pretty natural. The technique is described here: https://github.com/binaryage/cljs-oops#side-stepping-the-whole-externs-mess Please note that it does not require cljs-oops. You can use raw cljs interop as well.

dnolen12:03:37

@miikka no, file a JIRA for that

danielo51513:03:33

Thank you very much @darwin, I was thinking about something like that. I'll check it more deeply. Is there any guide about how to better write code with channels?

borkdude14:03:44

Could it be there was a bug fixed recently in transit-cljs with the newest CLJS? In an app I wrote some transit to a string and stored it in a javascript var. I got: unexpected token.

borkdude14:03:10

Going to try with the newest transit-cljs now

pesterhazy14:03:43

I heard there was a change to transit-cljs for compatibiltiy with the newest cljs

borkdude14:03:29

hmm, same error. I’ll see if I can reproduce something

borkdude15:03:36

ok, found the culprit. it’s some whitespace character from a source that JS can’t handle. I should probably escape this. Does transit offer something here?

borkdude15:03:10

I might get away with escaping the string produced by transit

borkdude15:03:11

it’s character 0x2029 that’s not being escaped

borkdude15:03:11

I’m doing a roundtrip to the server anyways, so this is going to work in my case:

(cheshire/encode “[Surgical Treatment of Refractory Chest Tumors Assisted 
by Cardiopulmonary Bypass].” {:escape-non-ascii true}) ;;=>
“[Surgical Treatment of Refractory Chest Tumors Assisted \u2029by Cardiopulmonary Bypass].”

borkdude15:03:23

to summarize, it didn’t have to do with recent changes in transit/cljs 😉

lwhorton15:03:13

does the :rename in ns-form support renaming macros? (:require namespc :refer-macros [foo] :rename {foo ns-foo})

justinlee16:03:36

i know it works with require-macros

lwhorton16:03:31

do you know by chance if it’s possible to use (require-macros ...) or (require ... :refer-macros []) outside of an ns form?

justinlee16:03:32

sorry i don’t. the ns form may be the most baroque and confusing thing in the whole language. i just cut and paste crap until it works

thheller16:03:17

@U0W0JDY4C ideally you don't use refer-macros ever. each .clj file that provides a macro should have a corresponding .cljs file that (:require-macros [itself]) in its ns form

thheller16:03:35

even if the cljs files doesn't have any other code

thheller16:03:55

than users of the ns can just :refer directly without caring whether its a macro or not

thheller16:03:16

but really you shouldn't.

justinlee16:03:05

don’t listen to thheller, live dangerously. what could go wrong?

lwhorton16:03:09

@U05224H0W my intent is to write a macro that essentially spits out some cljs code that makes a call to cljs.core/require [some.lib.with.macros :refer-macros [some-macro]] (some-macro ...args)

thheller16:03:21

yeah that doesn't work

lwhorton16:03:29

so that consumers of my macro dont also have to include a require to some other macro

thheller16:03:43

thats why you do the ns self-require trick

thheller16:03:56

require is completely static. there is absolutely no way to make it dynamic

lwhorton16:03:05

im not sure I follow the above: what is itself? if i have clj defmacro foo ... I should have a cljs file require-macros [macros :refer [foo]]?

thheller16:03:25

(ns some.thing (:require-macros [some.thing]))

lwhorton16:03:07

so the some.thing in require-macro some.thing is pointing to itself, not an identically-named clj file?

thheller16:03:30

the .cljs files has the require macros to an identically named .clj file yes

lwhorton16:03:11

and that somehow gets around also needing to include [some-other-macros-lib] in the cljs file?

lwhorton16:03:19

so long as the clj file has a require on that lib?

thheller16:03:49

not sure I understand the question

lwhorton16:03:03

i just want my cljs files to have to require 1 macro. that 1 macro (defined by me) has a dependency on a third-party lib macro. will this setup with the self-require and same-name allow me to only (from the cljs namespace) require my 1 macro, and not also the library dependency?

thheller16:03:31

I don't understand sorry. does YOUR macro emit code for the other macro? do you actually call it or does the user call it?

thheller16:03:59

YOUR code can (:require ...) the other macro and use it normally

lwhorton16:03:07

my-ns.cljs

(:require-macros [my-ns :refer [mac]])
(mac args ...)
my-ns.clj
(:require [ :refer [his-mac]])
(defmacro mac args
 … `(do (his-mac args))

thheller16:03:32

sure you can do that

thheller16:03:13

in my-ns just require other-ns as usual

lwhorton16:03:07

but can I also other.cljs

(:require-macros [my-ns :refer [mac]])
without also having to make the require
(:require-macros [my-ns :refer [mac]] [ :refer [his-mac]])

thheller16:03:38

other never has to require

lwhorton16:03:46

because wouldn’t the cljs code emitted essentially be (do ( ... and the cljs compiler would complain “hey where the heck is http://third.party?

thheller16:03:07

it wouldn't complain since my-ns required it

lwhorton16:03:26

and all this happens simply because the files are named the same, right?

thheller16:03:40

the filename doesn't matter. the ns matters and that it self requires

lwhorton16:03:42

because I’ve tried this with a separate my-ns.cljs and macros.clj, and it didnt work

thheller16:03:54

yeah because the ns is not the same

lwhorton16:03:18

is this documented somewhere? i feel like tens of people probably ran into this too

thheller16:03:01

should be. not sure.

thheller16:03:11

I made an example some time ago

thheller16:03:38

this question does come up a lot though so it should definitely be documented better

lwhorton16:03:03

this doesn’t seem to work 😕

lwhorton16:03:41

other.cljs throws a cljs-compile-time error of undeclared Var

lwhorton17:03:45

(this could just be that http://third.party isn’t doing something properly)

thheller17:03:26

@U0W0JDY4C you probably need to emit a fully qualified form just in case

thheller17:03:48

so instead of using (his-mac ..) in YOUR macro you use ( ...)

lwhorton17:03:08

how lucky of me — i named my macro the exact same as his 😞

thheller17:03:36

that doesn't matter if you use ns qualified forms

lwhorton17:03:49

(also in your example you use :refer (<-list->) which i’ve never seen, only ever vectors… and the ns-form documentation doesn’t have an example of that; is it the same?

thheller17:03:37

its the same yes. I always use lists out of habit

thheller17:03:40

the compiler doesn't care

lwhorton17:03:35

thats good to know, never seen it

lwhorton17:03:22

emitting a fully qualified doesn’t do it either .. it’s like has some other reference to (where elsewhere is defined in a cljs file. the http://third.party clj file does require [http://third.party] the cljs file properly) so my cljs compilation warns with is not defined.

thheller17:03:22

did your .cljs files require ?

lwhorton17:03:09

ack, does it have to?

lwhorton17:03:31

that’s what I was trying to avoid from the beginning .. i didnt want my consumers to have to know the implementation details of my macro

thheller17:03:12

this would be a heck of a lot simpler with some actual code at hand

thheller17:03:30

YOUR ns must require . the consumers doesn't need to

lwhorton17:03:57

let me make for you a real example

lwhorton17:03:25

; css/core.clj
(ns css.core
  (:require
    [cljss.reagent]))

(defmacro defstyled [sym element styles]
  (let [expanded (clojure.walk/macroexpand-all styles)]
    `(do
       (cljss.reagent/defstyled ~sym ~element ~expanded))))

;; css/core.cljs
(ns css.core
  (:require-macros [css.core :refer [defstyled]]))

;; app/consumer.cljs
(ns app.consumer
  (:require
    [css.core :refer [defstyled]]))

(defstyled foo :div
  {:font-size "100px"})

;; _> throws `(cljss.reagent/styled) is not defined`
;; error
(defstyled foo :div
  ^--- use of undeclared Var cljss.reagent/styled)
^-- use of undeclared Var reagent.core/as-element
and the culprit cljss.reagent macro is here: https://github.com/roman01la/cljss/blob/8e4ee3f0c65a0617a8910d75ad67acbb5b758e69/src/cljss/reagent.clj#L7-L8

lwhorton17:03:00

if I peek at the actual output of the js, which gives some insight into the macro’s output, I see:

goog.provide('css.consumer');
goog.require('cljs.core');
goog.require('css.core');
css.consumer.foo = cljss.reagent.styled( ...
and an expansion of the output:
(do (cljss.reagent/defstyled foo :div {:font-size "100px"}))
Because I’m trying to macroexpand from the cljs environment I dont have access to walk/macroexpand-all — so I cannot really see further down the rabbit hole.

lwhorton17:03:46

if I swap over the cljs code to clj for a second and eval with a macroexpand-all:

(do (do (def foo (cljss.reagent/styled ... blah blah ... reagent.core/as-element args ...
which is where both my errors are coming from. any ideas why those namespaces arent automatically available?

thheller17:03:50

I'm sorry but this is way too confusing for a slack thread. happy to look at an actual reproduction but I cannot follow this.

thheller17:03:15

I don't know what either of these macros do so I can't say what they might be doing wrong

thheller17:03:23

or if the problem lies somewhere else

lwhorton18:03:08

if you feel like it take a peek there, im still scratching my head

thheller18:03:36

need something I can actually compile and run ...

dnolen16:03:15

there’s a page on the ns form now - https://clojurescript.org/guides/ns-forms

dnolen16:03:26

it should cover everything

danielo51517:03:21

One question, maybe I'm too dumb

danielo51517:03:37

But how can I see a pretty printed object on the REPL ?

danielo51517:03:42

on a cli environment ?

danielo51517:03:49

I think it is a js object

zaphodious17:03:21

try js->clj ?

danielo51517:03:43

But I want to debug it, I need to see on the REPL how it looks like

danielo51517:03:03

Currently I'm getting #object[IncomingMessage [object Object]]

thheller17:03:33

@rdanielo best option is (js/console.log the-obj)

danielo51517:03:05

I was using println

Oleh K.17:03:23

(prn (js/JSON.stringify obj))

danielo51517:03:57

Is prn a shorthand for println?

danielo51517:03:12

I got confused because previously I get a string representation of the object

danielo51517:03:22

Maybe it was because it was a string

danielo51517:03:35

Not parsed Json

thheller17:03:46

prn is short for (println (pr-str))

jcr17:03:48

prn prints cl literals, println prints string representation

Alex Miller (Clojure team)17:03:59

^^ that, NOT a shorthand

Alex Miller (Clojure team)17:03:19

data vs human presentation

danielo51517:03:24

So does println coerce to string?

Alex Miller (Clojure team)17:03:50

all of the print methods create a string representation

Alex Miller (Clojure team)17:03:03

“coerce” might be a tighter meaning than you intend

danielo51517:03:12

I'll try prn and see how it looks

Alex Miller (Clojure team)17:03:35

generally pr should create things that can read by the reader (although there are a lot of caveats)

danielo51517:03:36

Another question. I read that it is better to not make conversions between js and cljs. So in order to find certain string within a deeply nested array inside a js object returned by an api is it better to use js interoperability? Native map and filter and find js methods?

richiardiandrea17:03:16

@rdanielo it might be premature optimization but I try to use goog.object if the shape of the js obj is known beforehand

richiardiandrea17:03:06

And avoid js->clj. The latter not able to convert JS weird things like lazy JS objects coming from DB queries for instance

richiardiandrea17:03:37

For the exact semantic, try (source js->clj) in the REPL

danielo51517:03:36

Doesn't cljs provide native ways of getting object props?

thheller17:03:23

the goog.object ns from closure is useful for this

danielo51517:03:31

How can I import goog.object?

Oleh K.17:03:36

I use

aget

danielo51517:03:44

That's it, aget

thheller17:03:17

(:require [goog.object :as gobj])

richiardiandrea17:03:17

aget usage is frowned upon 😄

richiardiandrea17:03:43

its semantic is geared towards array access

richiardiandrea17:03:22

meaning that it works with object by chance and if the impl changes you should not complain 😄

danielo51517:03:48

Woops, that's something to take in consideration

justinlee17:03:23

@rdanielo I might take a look at the cljs-oops library, which has lots of conveniences for accessing javascript objects

darwin17:03:38

@rdanielo looks like you are new here 🙂 here is a friendly list of libs to consider (I can help): https://github.com/binaryage/cljs-devtools https://github.com/binaryage/cljs-oops

justinlee17:03:56

^ what he said 🙂

darwin17:03:51

@rdanielo also to answer your earlier question, here is a nice article about core.async (it is clojure) but it gives you good overview and all the non-blocking “go” parts are applicable in cljs: https://www.braveclojure.com/core-async

jeaye18:03:41

cljs-oops is superb.

borkdude18:03:22

Why does cljs-ajax use https://google.github.io/closure-library/api/goog.json.html for parsing when they say it’s deprecated and use JSON.parse instead?

borkdude18:03:46

I was going to ask what goog.json brings instead of JSON.parse, but then I read it was deprecated

john18:03:48

according to this issue https://github.com/google/closure-library/issues/271 it can be set globally with goog.json.USE_NATIVE_JSON set to true

borkdude18:03:53

it does use JSON.parse by default

danielo51518:03:30

Cljs-oops looks nice

justinlee18:03:52

the “why” for gcl functionality that duplicates browser functionality is almost always to provide a uniform interface across browser implementations

danielo51518:03:58

But using a library just for access properties on objects seems like a deal breaker

justinlee18:03:13

@rdanielo you don’t need it, it’s just a small convenience

danielo51518:03:46

But I don't see a programátic way of accessing object properties

darwin18:03:06

goog.object is your friend

danielo51518:03:06

Like foo[bar]

dnolen18:03:10

that’s how you do it

dnolen18:03:35

ClojureScript itself uses it because there’s zero value in spending time writing code that’s already been written

danielo51518:03:40

Ok, OK, I'll investigate that 🙂

dnolen18:03:15

goog.object, goog.array, etc. are useful and we’re not going to waste effort supplying that stuff

justinlee18:03:30

@rdanielo even in native javascript, people write convenience functions to deal with nested property access: see property paths in lodash for example

justinlee18:03:45

that’s one the niceties cljs-oops provides, for example

danielo51518:03:54

Cljs-oops allows nested property access?

darwin18:03:27

cljs-oops validates[0] object access in dev mode[1] and provides friendly error messages, and in :advanced mode all that stuff gets collapsed into simple object access[2] (either via aget or goog.object.get) [0] https://github.com/binaryage/cljs-oops/blob/f74ce8d9f7e61426912160f858e71588bdfecf75/src/lib/oops/codegen.clj#L46 [1] https://github.com/binaryage/cljs-oops/blob/master/test/transcripts/expected/oget_dev.js [2] https://github.com/binaryage/cljs-oops/blob/master/test/transcripts/expected/oget_static_core.js

justinlee18:03:03

it’d be interesting if specter could compile down to native js accessors

danielo51518:03:33

@darwin so it basically adds 0 runtime overhead

darwin18:03:00

in :advanced code it writes the same code you would write by hand

darwin18:03:18

in :dev mode it writes code you would want to write but you are too lazy to do so

👍 4
justinlee18:03:02

and it’s worth pointing out that it is advanced-compilation safe, making it incredibly valuable for interop

darwin18:03:29

@lee.justin.m wait, I would not advertise that it solves the issue of knowing what is safe to rename and what not. the developer still needs to understand when he can use string names and when not. they need to understand closure advanced mode and how cljs compiler works

richiardiandrea18:03:36

is :infer-warning on by default if I use :infer-externs?

darwin18:03:25

@lee.justin.m see the FAQ, second question, for what I mean: https://github.com/binaryage/cljs-oops#faq

richiardiandrea18:03:50

ok it makes sense, I am trying to use the :infer-externs key and I having a bunch of:

Mar 29, 2018 11:48:58 AM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: out/inferred_externs.js:4: WARNING - name goog is not defined in the externs.
goog.string.StringBuffer;
^^^
worth a ticket?

dnolen19:03:22

pretty sure there’s already a ticket for this - I moved a bunch of externs inference tickets into critical priority

dnolen19:03:28

please check there first

dnolen18:03:36

warning inference has to be file local otherwise it would be very annoying

justinlee18:03:07

@darwin sorry that’s what i meant. i just wasn’t very precise

justinlee18:03:25

I assumed that’s why nobody recommended using dot accessors in this entire conversation

dnolen18:03:18

if you’re not using some random JS lib or dealing with JSON - you’re don’t need to bother with goog.object

darwin18:03:46

yep, he wanted to access his own JS object which is a payload he got over the wire, there he needs to use string names

justinlee18:03:42

But for symbol munging, you could do that with (.-prop js-obj) right? Or am I totally mixing things up here.

danielo51518:03:39

I want to write lense like functions, I don't want to access properties directly but using function composition

darwin18:03:16

in this case prop will get munged

justinlee18:03:17

right. hence, why I said “but for symbol munging”. i can say, as a relative newcomer, one thing that people often assumed I understood (although I did not) when I was learning about js interop is the advanced compilation issue. it’s so ingrained in the conversation sometimes

✔️ 4
currentoor18:03:23

anyone see blank/empty source mapping in prod in chrome?

pablore18:03:23

hi, how could i add npm dependencies with figwheel? I'm trying :npm-deps {:socket.io-client "1.7.2"} and then importing with :require [socket.io-client :as io] and :require ["socket.io-client" :as io] but none of them works

currentoor18:03:05

@pablore have you looked at sente? it’s the cljs version of http://socket.io

pablore18:03:19

can i connect it to a socket-io server?

danielo51518:03:39

I want to write lense like functions, I don't want to access properties directly but using function composition

pablore18:03:46

I need it to connect it to a http://socket.io server

currentoor18:03:53

i believe the npm-deps is still in alpha, have you tried checking cljsjs?

dnolen18:03:01

@lee.justin.m I think what’s being said here is still too vague

pablore18:03:16

cljsjs only has one particular version (the current one) of http://socket.io

pablore18:03:32

I need to use a older one

currentoor19:03:57

i see they have version 1.6.0 on clojars, https://clojars.org/cljsjs/socket-io

currentoor19:03:22

they probably would be willing to take a PR for 1.7.2

pablore19:03:48

thanks, but i'd prefer to have a working :npm-deps asap

pablore19:03:03

Im trying to convince my bosses how fast development in clojure is

dnolen18:03:54

if you’re not dealing with JSON or a foreign JS library there’s no reason not to use property access, that’s it

dnolen19:03:04

there’s no need to talk about munging or whatever

currentoor19:03:19

perhaps you could reach out to them and submit a PR, it’s usually a quick process and they might be able to accommodate you

justinlee19:03:22

yea i got that and didn’t mean to imply otherwise

justinlee19:03:25

in fact that’s the point i was trying to make, apparently unsuccessfully.

currentoor19:03:14

so, not trying to spam the channel, but has anyone run into the blank source maps problem? i seem to be stuck on this

dnolen19:03:31

but you need to be more descriptive in your reporting of the issue

dnolen19:03:36

is the source map file actually blank?

dnolen19:03:56

if it’s not blank then something is misconfigured wrt. serving it

currentoor19:03:37

my apologies, the source map file is not blank, i’m able to curl for it and chrome detects it

currentoor19:03:07

i’ll grab more details

john19:03:35

I use chrome. yeah, if you can make a repro I can help check it.

currentoor19:03:32

Both my compiled source file and source maps have the same exact parent directory. js/prod/ The bottom of my js/prod/arc.js file has this expected source map snippet.

//# sourceMappingURL=arc.js.map
My js/prod/arc.js.map file has this structure.
{"version":3,
 "file":"resources\/public\/js\/prod\/arc.js.map",
 "sources":[...],
 "lineCount":5652,
 "mappings": ...}
When an exception happens in prod in chrome, the stacktrace says its coming from dashboard.cljs. But when I click on dashboard.cljs it just shows a blank file, not my actual source code.

currentoor19:03:54

Also I am gzipping my assets and uploading them to an S3 bucket. But I’m guessing that is not the issue because everything else seems to be working.

currentoor19:03:43

Does anyone know of an example project with source maps and advanced compilation working in clojurescript? I could maybe look at that to figure it out.

dnolen19:03:50

@currentoor ah so you are confused how source maps works

dnolen19:03:02

you need all the original files

john19:03:08

perhaps we missed explaining that in the docs somewhere

dnolen19:03:44

using source maps in production isn’t really a thing for this reason

currentoor19:03:14

sorry about the confusion and thank you for explaining

john19:03:57

@currentoor did you end up trying to get those source maps added? If so, what steps did you take? I can add them to the docs.

john19:03:37

I suppose doing a :none build first, and then copying the *.cljs contents of :output-to into the :output-to directory of an :advanced build, and that might work.

currentoor19:03:29

I did not, I didn’t know that I’d have to expose all of our source code.

currentoor19:03:52

So I probably will not be doing this, in prod at least

john19:03:39

ah, k. Well, if you don't have a use case, perhaps there's no major use in building one. Would it have helped if the source-maps doc page mentioned "we don't package *.cljs files for :advanced mode as this could expose your source files in a production setting." or something to that effect

currentoor21:03:17

yeah that might be helpful

currentoor21:03:56

but right now it does tell me the file and line number that the exception originates from, it just doesn’t show the code in the browser

currentoor21:03:15

but i do have access to my own source code, so this is actually good enough

dnolen19:03:45

source maps are really for debugging advanced compilation locally

dnolen19:03:02

you can use them in production but it means you need to copy all the assets, not just your final stuff

dnolen19:03:11

like literally everything, JS files etc.

john19:03:46

more often then not, too, when your advanced compiling, you don't want to accidentally push your cljs files

currentoor19:03:13

that makes sense simple_smile

john19:03:35

Yeah, I don't see any reference to :advanced in https://clojurescript.org/reference/source-maps

richiardiandrea19:03:35

so should I hint of provide externs for ^export-ed symbols, if not, I don't see them exported in the generated .js

dnolen19:03:58

you don’t need externs for exported symbols

dnolen19:03:08

you’ve literally exported those names

richiardiandrea19:03:54

thanks David, the generated code I see is ma("my-ns.handlers.query-events",function...

richiardiandrea19:03:05

ma seems like renamed by the compiled

dnolen19:03:31

what does that have to do with anything

richiardiandrea19:03:34

right, let me explore more 😄

richiardiandrea19:03:55

I expected an export statement or something 🙂

dnolen19:03:02

ma is the export statement

dnolen19:03:12

but it’s irrelevant for what you’ve said above

dnolen19:03:21

why do you need externs for exports?

dnolen19:03:24

you don’t

richiardiandrea19:03:37

kk that answers the question

dnolen19:03:37

externs always means one thing

dnolen19:03:51

you know about something that Closure does not and you don’t want it to be renamed

dnolen19:03:59

exports is a completely different thing

dnolen19:03:10

you’re just exposing a name for JS callers

richiardiandrea19:03:16

yep, I think that's my end goal, thanks, to have a clear distinction of what is what is helpful (still digging into the JS world)

richiardiandrea19:03:23

David you are right the vars are regoularly expose from the object returned by require, no externs needed thanks!

danielo51521:03:50

Regarding cljs-oop: By default, oops assumes that you want to prefer static selectors and dynamic selectors are a mistake. That sentence does not make any sense to me. It's absurd

darwin21:03:16

Well, I’m not sure where to begin 🙂 Do you think you understand what I mean by “static” and “dynamic”?

john21:03:02

It assumes that you agree that such is the case. That dynamic selectors are a bad idea.

darwin21:03:14

oh, my english is the issue? feel free to rephrase it, I’m not a native speaker, I will gladly accept a PR

john21:03:23

I don't know, maybe he disagrees :)

justinlee21:03:25

I think it is pretty clear, frankly, but I can see how it might be misread. I think maybe if you said “dynamic selectors are unintentional” that might be more what you meant

justinlee21:03:43

you don’t mean a design mistake, you mean, more often than not, it’s a typo

danielo51522:03:12

Dynamic selectors are a powerful tool and I don't see why they are a design flaw

danielo51522:03:15

That is what I mean

danielo51522:03:24

Sorry for not being clear

danielo51522:03:32

It's not about the phrasing

danielo51522:03:41

It's the concept

danielo51522:03:58

I use dynamic selectors all the place, specially when doing FP

darwin22:03:57

I think you use static selectors and for some reason think you are using dynamic selectors. But we are off-topic here. We can move to private chat if you want to discuss it further.

danielo51522:03:01

Hello @darwin, I do use dynamic selectors, I'm pretty sure. Here is an example

danielo51522:03:24

(defn pick [prop o]
  (aget o prop))

(def pickUrl (partial pick "url"))

darwin22:03:22

well, this is not necessary in my book

darwin22:03:52

In general I would not want javascript engine to dynamically lookup keys

darwin22:03:47

if I really needed something like pick which is only sugar, I would write a macro

darwin22:03:46

I would use dynamic selectors only in cases when they are truly dynamic (e.g. formed as a result of user input, or some other dynamic data)

darwin22:03:06

over last few years I needed it just once

darwin22:03:27

…if I remember well

danielo51522:03:56

Macros, which expand to actual code are a very good approach for cases where you know stuff beforehand, it is something I don't have on JS, and it is definetively a good idea for this case

danielo51523:03:10

However, dynamic lookups are often useful

danielo51523:03:19

Why do you try to avoid them so badly ?

darwin23:03:50

I don’t avoid them, I simply write code which does not need them

darwin23:03:04

when I lookup keys I usually know the keys

darwin23:03:47

and there is nothing terrible using dynamic code path, but I want my tool to notify me that I’m doing it

darwin23:03:54

for cases when I overlook

darwin23:03:17

then I decide if it was really a mistake or I really want to use oget+ or similar dynamic version of access

darwin23:03:32

just safeguard/convenience

danielo51523:03:33

But what about function composition when you have tha same keys at different places ? You can define simple functions to get them and then chain at convenience

darwin23:03:31

ok, if you need it, go for it, I don’t needed, typically I have a js object at hand and I need to extract something from it, that is a static lookup

darwin23:03:14

I mean, generated javascript should look like obj[“key1”][“key2"] etc.

darwin23:03:30

not obj[val1][val2]

justinlee23:03:50

I have found it useful for when I typo (oget a js-obj) instead of (oget :a js-obj) (I think that’s the right example)

justinlee23:03:45

I think it also helps when you flip the argument order

darwin23:03:14

btw. I have to correct my statement about your dynamic pick. In :advanced mode google closure compiler might be smart enough to generate static code anyways (by inlinig the function). But I would not rely on it. When the function gets more complicated it might bail.

darwin23:03:06

and second note: all cljs-oops macros macroexpand its selectors, so using macros in places of parameters is still considered static selector if the macroexpansion generates static selector

darwin23:03:17

as you can see it is not a toy, I could generate dynamic code and call it a day, but I put a lot of effort into generating static code whenever possible

john00:03:07

Can you give some more details on your opinions about the pros and cons of static vs dynamic code?

john00:03:05

is this specifically about interop, and having static behavior between the cljs and the called js? Or more so about the static compilation of cljs in general, and keeping with that pattern?

darwin10:03:07

@U050PJ2EU well, I’m no expert in this, we are talking here about static key access, not dynamic vs. static code in general. I believe javascript engines can better optimize code when they see static key access. And you would normally write static access by hand when writing javascript. this has nothing much to do with cljs. Imagine you write in javascript obj["key"] why would cljs-oops-produced code look like var keyVal = "key"; obj[keyVal];? That would be a recipe to potentially end up with slower code.

darwin10:03:18

The second example is what I mean by “dynamic code”. Of course keyVal could be computed by a function or in a more complex way - this example is just ilustrative and maybe V8 can optimize it as well, who knows.

tbaldridge03:03:51

Yeah, it can, that sort of optimization is pretty basic JIT stuff.

tbaldridge03:03:15

Cases where stuff could be dynamic, but isn't at runtime that is.

gfredericks23:03:17

Is there a cljsbuild or compiler option for turning off assertions?

dnolen23:03:19

:elide-asserts

gfredericks23:03:31

ooh nice, thanks