Fork me on GitHub
#clojurescript
<
2018-09-26
>
zalky13:09:44

Hi folks, ran into a problem last night and trying to clarify my understanding of the cljs compilation process. I'm bundling a js library via webpack and pulling it in as foreign-libs in an attempt to bypass advanced compilation. The Clojurescript docs clearly state that "foreign libraries are included in your final output, but are not passed through advanced compilation." My hope was that by avoiding advanced compilation on the js lib, I would not have to deal with externs, and get a more reliable build in advanced compilation mode. However, even as foreign libs, the names in the js lib still get renamed, and nothing seems to work unless I add the :infer-externs compiler options to true. Even with :infer-externs set, I've come across the "<some-method> is not a function" error, which I was only able to resolve by using string names for the methods. It appears everything is still being renamed in the js foreign lib as part of the compilation process, which is confusing because I thought that renaming is part of advanced compilation mode, and foreign libs were supposed to bypass that. Any thoughts that might help clarify things?

valtteri13:09:03

@zalky afaik the thing is that your code goes through advanced compilation even though foreign lib doesn’t. Any references you make to foreign lib in your code gets munged and that’s why it breaks if you don’t have externs to tell closure compiler “please don’t munge these”. :infer-externs is a nice tool to do the externs part for you but it’s not perfect and sometimes you need to provide type hints to make it work. You can add (set! *warn-on-infer* true) in the beginning of your namespace to get compile warnings when externs inference fails and get notified when type hints are needed

dnolen14:09:47

@zalky your expectations were off

dnolen14:09:14

anything that doesn’t pass through advanced compilation needs externs period

dnolen14:09:29

doesn’t matter if it’s a foreign lib or some script tag you’ve inlined on that page

dnolen14:09:33

it’s all the same

dnolen14:09:50

re: using string names - don’t do that

zalky14:09:01

Gotcha, I think I see now that it's the munging on the cljs side that is the problem, and unavoidable with advanced compilations.

dnolen14:09:02

:infer-externs true should work

dnolen14:09:34

however you may write some interop with a foreign lib and there’s a few cases where you must hint the compiler

dnolen14:09:41

(.foo js/foo …) can be inferred

dnolen14:09:46

because js/...

dnolen14:09:58

(:require [foreign.lib ..]) can be inferred

dnolen14:09:04

because you defined it as foreign

dnolen14:09:08

what cannot be inferred

dnolen14:09:17

(fn [x] (.foreignMethod x))

dnolen14:09:31

(fn [^js x] (.foreignMethod x))

dnolen14:09:47

that’s all you need

dnolen14:09:12

I’ve been working with non-trivial foreign libs on a client project and I’ve encountered no issues with :infer-externs so far myself

zalky14:09:24

ahh, I see, that is exactly the case that was failing...

dnolen14:09:27

so I will be skeptical this won’t work for you unless you show me some examples 🙂

zalky14:09:59

I'll try the type hinting

zalky15:09:06

@dnolen, success! Thanks for the pointer that's really helpful. However, I notice you do not appear to need the type hint with every (fn [x] (.foreignMethod x)). So is this context dependent on the particulars of x?

thheller15:09:05

@zalky nah its just the name itself. if its found in any externs at all it won't be renamed so its just accidental they don't get renamed. it is safer to always typehint values so you don't run into issues when the other externs get removed/changed for some reason

zalky16:09:41

@thheller, thanks, that's also very helpful to know!

lwhorton16:09:01

if I think I found a cljss compiler bug where do I report this?

lwhorton16:09:49

we have a case where very occasionally (as in, maybe 1 in a few hundred) the generated javascript is different… and the different case actually causes a runtime error.

richiardiandrea16:09:12

@lwhorton you should probably ask first in #cljs-dev then in case report it to Jira

dnolen16:09:39

@lwhorton important to make a minimal reproducer for something like that, but yes take it to #cljs-dev

lwhorton17:09:48

yea i just listened to your talk about simpler tools and tools deps for producing minimal cases 🙂 ill attempt it later today

slipset20:09:17

This is probably a faq, but my simple googling hasn’t helped me, so I’ll try

slipset20:09:41

With Clojure and Lein, it’s quite simple to add java-files to a Clojure project.

slipset20:09:30

Is there a similar feature with Clojurescript projects, eg that you could specify js-source-path and have them compiled with your clojurescript?

slipset20:09:26

I know you can have js-files around in figwheel.main, but they need to do the goog.provide() thing.

dnolen20:09:30

:libs but those files must be writte n in Google Closure style

dnolen20:09:50

it’s quite useful, but not many people do it

slipset20:09:50

So no ES-6 style js

dnolen20:09:13

technically supported but it’s not heavily used so your mileage may very

slipset20:09:27

It would be kind’a neat if you’re porting an existing js-app to cljs.

dnolen20:09:37

yes that’s why it exists

slipset20:09:40

anywhere I can read up on that?

dnolen20:09:45

not really

dnolen20:09:50

it’s not a heavily used feature

slipset20:09:38

Or just try out and play with it until it works or I get tired?

slipset20:09:45

Which is perfectly fine for me 🙂

dnolen20:09:55

pretty much

dnolen20:09:15

and if I remember there are some limitations

dnolen20:09:43

you can import ES6 stuff, but different syntax for Closure stuff? Also can’t import ClojureScript stuff?

dnolen20:09:55

suffice to say, cutting edge

dnolen20:09:08

and it’s languished because it’s not solving a problem a lot of people actually have

dnolen20:09:42

for what it’s worth I don’t find ES6 syntax meaningful or compelling

dnolen20:09:51

I wrote Transit-js in regular Closure style

dnolen20:09:04

transit-js is loaded by transit-cljs

dnolen20:09:14

and the whole thing can go through advanced compilation and no one is the wiser

slipset20:09:30

That’s a shame, I almost have this problem now. We have a huge js-SPA at work which we want to “reassemble”. It’s written with all kinds of modules, ES-6 and require.js at least.

uosl16:09:40

A bit late reply, but have you looked at shadow-cljs? I think it should give better interop support with JS https://shadow-cljs.github.io/docs/UsersGuide.html#_language_support

slipset07:10:20

Thanks! I’ll have a look.

dnolen20:09:32

pretty much the only reason transit-js would have been in ES6 would be to get JS people to contribute

dnolen20:09:38

but even that doesn’t really make much sense

dnolen20:09:44

transit-js gets like 1 commit a year

dnolen20:09:51

it works, it’s fast

slipset20:09:27

So my use case would be to write the core in cljs and “port” existing parts/views from the old app over to the cljs-app without having to rewrite everything into cljs.

dnolen20:09:28

the best kind of library is the one you don’t have to work on

dnolen20:09:06

@slipset right I understand the use case - but those are the caveats

dnolen20:09:18

happy to see somebody fix it up if there are holes when you try it out

slipset20:09:19

Ok, thank you for your time. And Clojurescript!

Digital Baboon20:09:53

Hey guys! Do you have any recommendation on how to get JSON from a REST API endpoint (url) to a ClosureScript vector? (I'm guessing, as I want to display the data by looping it with OM. Currently I've tried to get JSON data with ajax.core (GET "url"), which returns the data, but as a text string not as JSON

pesterhazy20:09:20

(-> my-string js/JSON.parse js->clj)

justinlee20:09:35

ajax.core has a built in json response handler

justinlee20:09:58

I believe you can just do :format :json — the library is a little confusing because it has a “simple” style and a “complex” style. you just need to read the docs and pass the magic words to it depending on how you are invoking it

justinlee20:09:40

it is possible to customize the response handler too, which I did so that it doesn’t keywordize numeric keys

pesterhazy20:09:32

but really, (-> (js/fetch url) (.then #(.json %)) (.then js->clj) (.then (fn [r] (do-something-with r))) is usually easier, no library needed

Digital Baboon20:09:00

It's my second day with Closure (new job, must master it) and so what I'm trying to do is instead of hardcoding (defonce app-state (atom {:data[{.... , I would get all that with a call.

Digital Baboon20:09:05

I'll try it out

justinlee21:09:25

is there a better way of creating fully qualified keywords from unqualified keywords than this? (map #(keyword (namespace ::x) (name %)) (keys {:a 1 :b 2}))

mfikes21:09:44

You can omit the name call, as it’s built into keyword

mfikes22:09:28

Dunno if (str *ns*) buys you anything over (namespace ::x)

justinlee22:09:24

@mfikes can you do that in cljs? it seems to eval to nil

mfikes22:09:29

Right, forgot. That’s only for self-hosted.

shaunxcode22:09:43

when using transit what is the best way to keep 1.0 as a float and not have it turn into 1? Is a custom tagged value the only real way forward?

idiomancy23:09:06

Hey, I know this gets asked every now and then, but I can't really find the answer. What is a good client side router for cljs? Secretary's current build is apparently failing and hasn't been updated in 2 years

idiomancy23:09:26

hmm nice! I'll check that out! Anything in particular you like about it? Had you tried other ones?

idiomancy23:09:53

huh, certainly looks great