Fork me on GitHub
#clojurescript
<
2017-05-08
>
iku00088801:05:04

Is it possible to compile a single cljs file to a single js file that does not require goog.base?

john02:05:19

@iku000888 I believe compiling in :advanced mode, and perhaps :whitespace mode, will produce a single file, which inlines any goog.base deps into a single js file.

john02:05:13

But I believe you'll need to specify your main ns with :main in your compiler options.

iku00088802:05:23

:main sounds like the trick! Will give it a try. (I did :advance and I was still seeing the goog.* stuff showing up). Thanks!

john02:05:50

and note that in project.clj you do not need to prepend the main namespace with an apostrophe

iku00088802:05:40

Hrm Somehow this gets prepended to the output...?

gfredericks02:05:36

I see maven central doesn't have any releases for org.clojure/google-closure-library since june 2016; will that be updated eventually? There's a pretty serious bug in the goog.math.Integer class.

john02:05:56

@iku000888 I believe that's normal. Always there.

anmonteiro02:05:07

@gfredericks I would advise to bring it up in #cljs-dev or JIRA

gfredericks02:05:32

@anmonteiro thanks, didn't know about #cljs-dev

john02:05:10

@iku000888 np! Have fun 🙂

iku00088808:05:45

What is the best way to introspect a

#object[HTMLCollection [object HTMLCollection]]
?

thheller08:05:27

(js/console.log the-coll) or (js/console.dir the-coll)

iku00088808:05:53

I tried js->clj, but seems to not change the output...

thheller08:05:25

those are native objects that have no equivalent in clj

iku00088808:05:07

So would that imply I only get the dom api's to speak to them and nothing else?

iku00088808:05:30

Oh and @thheller thanks for the tips!

urbanslug13:05:55

Hey, how can I make the functions say_hi and say_hello js functions that I can call in node?

(ns my-ns.core
  (:require [cljs.nodejs :as nodejs]))

(nodejs/enable-util-print!)

(defn main [& [a name]]
  (case a
    "hi" (say_hi name)
    "hey" (say_hello name)
    (println "Nothing"))
  {"a" 1})

(defn ^:export say_hi [name]
  (println "Hello World!"))

(defn  ^:export say_hello []
  (println "I said"))

(set! *main-cli-fn* main)
;;(set! (.-say_hi main) 'say_hi)


To do something like
node -e 'var my-package = require("./target/js/milia.js"); my-package.say_hello()'

urbanslug13:05:20

or better yet how can I compile a cljs package into a node js one

urbanslug13:05:40

The methods people advocate are just not covering exporting multiple functions

dnolen13:05:07

@urbanslug that looks right to me

urbanslug13:05:35

Weird, it says "my-package.say_hello is not a function"

urbanslug13:05:13

lol why did I even bother leaving out the name if I was going to leak it on the require. OpSec fail

dnolen13:05:17

var my-package = ... is not valid JavaScript

dnolen13:05:04

note the - illegal character in the identifier

urbanslug13:05:56

OK in this case the name is actually different and doesn't have the -

urbanslug13:05:20

OK since hiding the name was for nothing let me be clearer

dnolen13:05:21

then other problem is

dnolen13:05:36

goog.exportSymbol(...) has nothing to do with Node.js packages

dnolen13:05:45

that’s all ^:export does

dnolen13:05:04

you need (set! js/module.exports ...)

dnolen13:05:20

or whatever the convention is for Node.js (I always get it wrong)

urbanslug13:05:24

@thheller That doesn't export any functions

thheller13:05:40

see the configuration, you configure the exports there

thheller13:05:12

:exports {:say_hi my-ns.core/say_hi}

thheller13:05:05

no need for anything else in the code

urbanslug13:05:25

hmmm trying to figure out which approach is better

thheller13:05:45

the issue with the (set! js/module.exports ...) hack is that it doesn't work in :none. so it will be slower during development

urbanslug13:05:21

OK one sec, let me play around with them

urbanslug14:05:16

OK forgive my total newbiness here but I can't figure out in the set! way of doing things shouldn't this work?

(ns milia-wrapper.milia
  (:require [cljs.nodejs :as nodejs]))

(nodejs/enable-util-print!)

(defn main [& [a name]]
  (case a
    "hi" (say_hi name)
    "hey" (say_hello name)
    (println "Nothing"))
  {"a" 1})

(defn ^:export say_hi [name]
  (println "Hello World!"))

(defn  ^:export say_hello []
  (println "I said"))

(set! js/milia-wrapper.milia.exports {:say_hello milia-wrapper.milia/say_hello})
and I should be able to do something like this
node -e 'var milia = require("./target/js/milia.js"); milia.say_hello()'
but still say_hello is not a fn

urbanslug14:05:44

Oh it doesn't work in node

thheller14:05:59

@urbanslug only module.exports is recognized by node when using require

urbanslug14:05:14

My optimization level is none

urbanslug14:05:03

So node -e 'var milia = require("./target/js/milia.js"); milia.exports.say_hello()'

thheller14:05:36

no, none of this works in :none sorry

urbanslug14:05:18

What did you mean by only module.exports is recognized?

urbanslug14:05:29

Did you actually mean exports should be included?

thheller14:05:32

(set! js/milia-wrapper.milia.exports {:say_hello milia-wrapper.milia/say_hello}) that part

thheller14:05:10

only works as js/module.exports not js/milia-wrapper.milia.exports

thheller14:05:49

but even then it won't work in :none, that is because node expects one file per require but CLJS is split into many files in :none

urbanslug14:05:50

@thheller hmmm

(set! js/module.exports {:say_hello milia-wrapper.milia/say_hello})
won't work for me using
node -e 'var milia = require("./target/js/milia.js"); milia.say_hello()'
even with optimization being advanced I guess I'll just try the way that you pointed out and figure out why Nolen's method isn't working for me later,

thheller14:05:17

ah :advanced doesn't work because you are exporting a CLJS map

thheller14:05:36

(set! js/module.exports #js {:say_hello say_hello}) that should do it for :advanced

urbanslug14:05:58

@thheller hmmm so I have to use your build tool to get/use the :exports in my project.clj

thheller14:05:41

yep, others don't support this feature

urbanslug14:05:42

Thanks a ton

reefersleep16:05:59

I upgraded my project to [org.clojure/clojurescript "1.9.521" :scope "provided"]. Now a (let [{:keys [a b] :as full} (my-own-function)] (prn full)) prints {[:a "a's value"] [:b "b's value]} rather than {:a "a's value" :b "b's value"}. Did destructuring change?

reefersleep16:05:07

I checked the final value of (my-own-function) by printing it before returning it, and it looks just fine, that is, {:a "a's value" :b "b's value"}, and is then magically converted by the destructuring or let or something.

anmonteiro16:05:44

@reefersleep what version did you upgrade from?

reefersleep16:05:30

Here's a simplified example:

(let [{:keys [hey girl] :as full} {:hey "yo" :girl "dude!"}]
               (prn hey)
               (prn girl)
               (prn full))



nil
nil
{[:hey "yo"] [:girl "dude!"]}
nil

anmonteiro16:05:04

cljs.user=> (let [{:keys [hey girl] :as full} {:hey "yo" :girl "dude!"}]
       #_=>                (prn hey)
       #_=>                (prn girl)
       #_=>                (prn full))
"yo"
"dude!"
{:hey "yo", :girl "dude!"}
nil

anmonteiro16:05:12

^ in Lumo, which is running 1.9.521

anmonteiro16:05:28

right, my suspicion is you have an old analysis cache that you need to clean

reefersleep16:05:32

I've no idea what's going on 🙂

reefersleep16:05:07

I guess the first thing to try is to restart Emacs, I'll give it a go.

anmonteiro16:05:15

that’s not gonna do anything

anmonteiro16:05:55

@reefersleep this is unrelated to Emacs. Try do delete your output directory and restart your build

reefersleep17:05:38

I'll try that. (I had restarted the repl, which runs figwheel, but not deleted the output dir, and I can see why you suggest that 🙂 )

anmonteiro17:05:12

Yeah, the point of the analysis cache is to make your compilation times faster even if you restart the REPL

reefersleep17:05:56

I'm unsure of what I'm looking for. Tried mv'ing public/js/out to out.bak, which prompted the build process to create a new out folder, but this did not make any difference.

noisesmith17:05:04

the typical issue is that the cached partially compiled code mixes with the new code in an unexpected way that creates nonsense results; the fix to this is to shut down any auto-builder, run lein clean (or whatever else it takes to completely erase all partially compiled artifacts), and the nrestart the builder

noisesmith17:05:22

this is commonly a problem after changing dep versions (in my experience)

anmonteiro17:05:07

note that lein clean is not gonna do what you expect it to do

noisesmith17:05:29

@anmonteiro it does for me- because I set up my clean targets to do so, I guess I can’t generalize that

noisesmith17:05:41

but yeah, the main thing is to destroy all compilation artifacts

noisesmith17:05:41

having a default config where clean fails to remove an output of a compiler driven by lein seems like a misconfiguration to me

reefersleep17:05:43

lein cleandid not do the trick in my case.

john17:05:43

rm some of them artifact output folders

reefersleep18:05:45

I've tried to remove all of those that I could find, lein clean and re-run figwheel, nothing has helped so far.

reefersleep18:05:19

Ah, just found a hidden folder I missed

reefersleep18:05:02

Yes! Finally managed it. /.cljs_rhino_repl/ was the (last?) folder that I needed to remove. Thank you very much, @anmonteiro, @noisesmith and @john !

noisesmith18:05:41

if that was my project I would add that dir to lein’s clean targets

noisesmith18:05:08

I don’t like having caches of compiled code that lein doesn’t obliterate when you run clean

reefersleep19:05:06

I'll run with that! 🙂

reefersleep19:05:14

wonder how the folder was generated to begin with, it wasn't re-generated when I re-ran figwheel

timgilbert19:05:29

Hi all, I'm messing with the new :npm-deps feature and I keep getting errors about transitive NPM dependencies. Currently running with a deps.cljs that looks like this:

{:npm-deps {:draft-js                 "0.10.0"
            :draft-js-import-markdown "0.2.1"
            :draft-js-export-markdown "0.2.2"}}
...and I get an error Cannot find module 'react' from '/Users/foobar/node_modules/draft-js/lib'

timgilbert19:05:17

My project also uses reagent, which is where I'd like to pull the react dependency from. Do I need to declare it as a foreign-lib or something?

anmonteiro19:05:43

@timgilbert you probably just need to specify react too in :npm-deps

anmonteiro19:05:51

if it’s an NPM peer dependency

anmonteiro19:05:12

currently no way to make it play nice with Reagent if there’s no lib support for it

timgilbert19:05:34

Ok. I was hoping I could actually just give it :draft-js-import-markdown and have it pull in draft transitively too

anmonteiro19:05:42

it’ll pull transitive dependencies

timgilbert19:05:47

Hmm, looks like the NPM dependency tree is broken though

anmonteiro19:05:48

but not peer dependencies

timgilbert19:05:02

Ah, ok, that makes sense

timgilbert19:05:14

Is there a similar mechanism to the lein/clojure :exclusions thing, so I can tell NPM "this will be loaded from elsewhere, don't worry about it"?

noisesmith19:05:41

if some dep will be provided by other software at runtime, that’s what the :provided scope in project.clj is for

noisesmith19:05:14

easy fix for someone who has a moment, lein help :sample doesn’t mention the :provided profile, maybe it should?

noisesmith19:05:13

oh - maybe what you are describing is different though

john19:05:13

what's the correct way to optionally handle whether someone passed something into & args? Is it (if (first args)... or (if (empty? args) ... or is something with (apply afn stuff args) supposed to always work? I worry about a nil being passed in.

noisesmith20:05:18

I’d test if args is empty - it always wraps all the args in a collection, so if they passed nil you would get [nil] which is not empty

john20:05:45

Which is why I like the (first args) method, since it returns nil if there is a nil or if it is empty.

noisesmith20:05:39

that’s your problem though - if something calls (fn [a & args] ...) with (a 1 nil 2) calling first misleads you

noisesmith20:05:56

checking if args is empty is reliable though

john20:05:18

that's why I felt so uneasy

john20:05:23

Sage advice

john20:05:21

I'm seeing a Delay deftype and related documentation in the source that references a delay function, which doesn't appear to exist. Is delay not yet implemented?

john21:05:25

That's the one that the docs bring me to

jr21:05:39

that file is for core functions

jr21:05:08

the file I linked to are core macros

gfredericks21:05:40

What's the right way to require/import a closure class (e.g. goog.math.Integer) so that I can call static methods, refer to static fields, and extend the class to protocols?

gfredericks21:05:19

I used to have (:require [goog.math.Integer :as int]) and then use int/ for the former cases and goog.math.Integer fully qualified for the latter

gfredericks21:05:37

but that feels terrible and has started giving warnings in more recent versions of cljs so I assume there's a better way to do this

nooga21:05:30

clojurescript complains: Use of undeclared Var cljs.core/and at line 13

fbielejec21:05:02

hit, I have an endpoint where I can upload a text file with curl like this: curl -X POST -H "Content-Type: multipart/form-data" -F "file=@/resources/speciesDiffusion.tree" now I need to send a similar request from a browser, but

(ajax/ajax-request
      {:uri (str "" "/continuous/tree")
       :method :post
       :params {:treefile content}
       :handler #(println %1)
       :format (ajax/text-request-format)
       :response-format (ajax/json-response-format {:keywords? true})})
gives me a (nicely json converted) error response: [false {:status 500, :status-text , :failure :error, :response {:timestamp 1494279686227, :status 500, :error Internal Server Error, :exception org.springframework.web.multipart.MultipartException, :message Current request is not a multipart request, :path /continuous/tree}}] so probably the content-type headers is not correctly set

mikerod22:05:16

I’m sort of surprised this is hard to find, but does anyone know of a good way to configure routing (like via secretary) for a single-page app (SPA) in a way that can deal with browser refreshes, as well as someone just putting a full URL path into the location bar?

noisesmith22:05:02

@mikerod I don’t know if there’s something fancier, but what my app does is make sure that any state that should persist across a refresh (or bookmarking, or following a link) needs to be injected into the fragment data of the page

noisesmith22:05:34

since the data is in the fragment, it doesn’t cause a new load of the page just because you changed the url

noisesmith22:05:51

because it’s in the fragment, the back button and link sharing etc. are less likely to break

noisesmith22:05:29

I made a small library that takes edn data and translates to base64 and puts it in the fragment (with a decoder that goes with, of course)

mikerod22:05:03

I haven’t even had luck doing a page refresh while attempting to have things after the fragment

mikerod22:05:19

I’ve actually been seeing it just load the page ignoring the fragment

noisesmith22:05:29

@mikerod weird -I have never seen a refresh alter the fragment

mikerod22:05:44

I’ve noticed that in an angular app I was able to do refreshes against a fragment and also to directly plug the fragment into the location bar and go

noisesmith22:05:46

wait, who are you expecting to act on the fragment?

mikerod22:05:49

So I wonder what is happening there

noisesmith22:05:13

the fragment is for your app, you have to do something with it (in my case I parse it and inject it into the app state)

mikerod22:05:15

e.g. for that one location is localhost/thing/#/stuff

mikerod22:05:30

If I refresh there It seems to ignore #/stuff

mikerod22:05:51

It doesn’t even call the navigate listener, so I must be doing something wrong

mikerod22:05:58

I just wish there were some clear docs on this issue

mikerod22:05:07

I guess it is just tricky or something

noisesmith22:05:18

OK - when my fragment is changed, goog.history definitely gets invoked

noisesmith22:05:23

that’s what drives my router

mikerod22:05:51

I’ve seen accountant which seems to try to address the overall problem (I think). However, it doesn’t work well for someone to manually put the location in the location bar and go (or I don’t know how to use it right if it does)

mikerod22:05:29

I probably have more than 1 probleem

noisesmith22:05:31

I just hook the my router into goog.history and then parse data out of the fragment… I don’t feel like I did anything that unusual

mikerod22:05:35

so I need to isolate things

mikerod22:05:55

I followed the basic template I’m seeing other places.

mikerod22:05:09

The main thing I don’t see a clear path forward on is browser refreshes behaving sanely

mikerod22:05:22

I think I can get back, forward, and fragment changes to do what they are supposed to

noisesmith22:05:45

for refresh, I had to make sure that app load propagated state via the same code that the router does

noisesmith22:05:10

and that no page implicitly relied on state initialized from another page

mikerod22:05:19

interesting

noisesmith22:05:00

that plus ensuring that all “bookmarkable” state and state that should be altered by forward / back areas was reified into the fragment data

noisesmith22:05:09

then you get a mostly-not-broken app

noisesmith22:05:22

(in terms of url / navigation / refresh)

noisesmith22:05:29

I should really make a blog post about how we did this

mikerod22:05:55

would be interesting to see. I haven’t seen blogs really answering these points for me so far. Been tryign to search quite a bit.

darwin22:05:59

I’ve done this once with secretary but I don’t remember the details (warning this code is 2+ years old): https://github.com/darwin/faceboard/blob/master/frontend/src/faceboard/router.cljs

mikerod22:05:26

interesting, thanks for the links