Fork me on GitHub
#clojurescript
<
2018-07-06
>
Garrett Hopper00:07:41

I've run into a few issues regarding cljs.main, :npm-deps, and :foreign-libs. Let me know if any more information is needed. If there aren't already Jira tickets for these, I can create them. If there are, links are appreciated, so I can follow them. 🙂 Note: I've also tested these with the latest master (`1.10.358`).

Garrett Hopper00:07:42

1. When using -co, cljs.main seems to default to repl mode (`-r`), however it exits immediately. It does, however, open up http://localhost:9000/. clj -Sdeps "{:deps {org.clojure/clojurescript {:mvn/version \"1.10.339\"}}}" -m cljs.main -co "{}" && echo exited $? exited 0 Does this command immediately exit for others? (It doesn't matter if the -co has properties or is an empty map.)

Garrett Hopper00:07:43

2. In order to use :npm-deps from the command line, it seems -c is required. (`-c` then -r) Starting a REPL with left-pad as an example: clj -Sdeps "{:deps {org.clojure/clojurescript {:mvn/version \"1.10.339\"}}}" -m cljs.main -co "{:npm-deps {left-pad \"1.3.0\"} :install-deps true}" -r

cljs.user=> (require '[left-pad])
cljs.user=> (left-pad "test" 5)
ReferenceError: module$home$garrett$node_modules$left_pad$index is not defined
         (<NO_SOURCE_FILE>)
I recall something about module names being in two different formats in a recent Jira ticket. This is looking for the full module$home$garrett$node_modules$left_pad$index (I'm running this in my home directory /home/garrett.) instead of the shorter/simpler module$left_pad$index. I assume this is a related issue. A workaround is to use -c. -c requires :main, so let's make the main file:
mkdir -p src/example
echo "(ns example.core)" > src/example/core.cljs
Starting a new REPL: clj -Sdeps "{:deps {org.clojure/clojurescript {:mvn/version \"1.10.339\"}}}" -m cljs.main -co "{:npm-deps {left-pad \"1.3.0\"} :install-deps true :main example.core}" -c -r
cljs.user=> (require '[left-pad])
cljs.user=> (left-pad "test" 5)
" test"
Note: It seems that once the above command has been run, the original -r-only command can be run. (Removing the out folder causes the issue again.) Original REPL invokation after previous 'fix': clj -Sdeps "{:deps {org.clojure/clojurescript {:mvn/version \"1.10.339\"}}}" -m cljs.main -co "{:npm-deps {left-pad \"1.3.0\"} :install-deps true}" -r
cljs.user=> (require '[left-pad])
cljs.user=> (left-pad "test" 5)
" test"

Garrett Hopper00:07:44

3. Using :foreign-libs along with :npm-deps seems to be broken. Starting a REPL with both :npm-deps and :foreign-libs (without the -c/`:main` 'fix' for now): clj -Sdeps "{:deps {org.clojure/clojurescript {:mvn/version \"1.10.339\"}}}" -m cljs.main -co "{:npm-deps {left-pad \"1.3.0\"} :install-deps true :foreign-libs [{:file \"" :provides [\"google.api\"] :global-exports {google.api gapi}}]}" -r

cljs.user=> (require '[left-pad])
This fails of course, but the following call fails regardless of if this is called.
cljs.user=> (left-pad "test" 5)
ReferenceError: module$home$garrett$node_modules$left_pad$index is not defined
         (<NO_SOURCE_FILE>)
When this is called after the (require '[left-pad]) call, it fails.
cljs.user=> (require '[google.api])
java.io.FileNotFoundException: /home/garrett/https:/apis.google.com/js/platform.js (No such file or directory)
... (See  for full stack-trace)
If the :foreign-lib library is required before the :npm-deps dependency, it will work. Previous REPL invokation (with -c/`:main` 'fix' to make left-pad work, though the result is the same with or without—`google.api` can be required.): clj -Sdeps "{:deps {org.clojure/clojurescript {:mvn/version \"1.10.339\"}}}" -m cljs.main -co "{:npm-deps {left-pad \"1.3.0\"} :install-deps true :foreign-libs [{:file \"" :provides [\"google.api\"] :global-exports {google.api gapi}}] :main example.core}" -c -r
cljs.user=> (require '[google.api])
cljs.user=> (require '[left-pad])
cljs.user=> (left-pad "test" 5)
" test"

mfikes02:07:17

@ghopper https://dev.clojure.org/jira/browse/CLJS-2666 might be a generalization of your item (2) above

Garrett Hopper14:07:59

Nice, -d out does indeed fix the issue.

mfikes02:07:29

For item (1), -co is an init option, its behavior is analogous to clj vs. clj -e 3 vs. clj -e 3 -r

Garrett Hopper14:07:20

That makes sense. Shouldn't it either throw an error or be ignored instead of what it's doing now?

mfikes02:07:22

Item (3) seems like a bug (which I don’t recall seeing in JIRA)

justinlee03:07:00

is there a way to write this (apply js/Math.min '(0 1 2)) without abusing the . syntax? i.e. I usually invoke functions like this using the (.min js/Math 0 1 2) syntax, but I can’t figure out how that works with apply

souenzzo09:07:58

(js/Math.min.apply nil #js [2 3])

souenzzo09:07:55

now I understand what you ask Not sure if you can do it. . is a special form. In cljs we can do (apply (.-min js/Math) #js [1 2 3])

justinlee15:07:38

@souenzzo thanks! I didn’t think about using the .- form. this is what i ended up with (defn mymin [& xs] (apply (.-min js/Math) xs)) seems to work

đź‘Ť 4
ClashTheBunny03:07:42

Hey folks, I’m trying to read and process a CSV in a macro so that I just get edn loaded into my clojurescript, but I keep getting arity <number of lines of csv + 6> errors. Anybody know what’s going on? In cljs:

(def csv-data
  (dm/read-csv-file "data/awesome.csv")))
In dm.clj:
(ns demo.macros
  (:require [clojure-csv.core :as csv]))

(defmacro read-csv-file [file]
  (doall (csv/parse-csv (slurp file))))
I see the file parsed in the compiled js, but I don’t know how I’m reading it wrong.

ClashTheBunny03:07:41

The generated Javascript has this form:

devcards.core_card.csv_data = cljs.core.first((function (){
   var G__31138 = cljs.core.PersistentVector.fromArray(["things","in","the","csv"], true);
......
var fexpr__31137 = cljs.core.PersistentVector.fromArray(["other","things","in","csv"], true);
return (fexpr__31137.cljs$core$IFn$_invoke$arity$1090 ? fexpr__31137.cljs$core$IFn$_invoke$arity$1090(G__31138,......,G__32227));
})());

ClashTheBunny03:07:02

I have the feeling I’m not getting the data type I expect to be getting from this, but I’m not exactly sure how to find out and what do do about it.

ClashTheBunny03:07:02

What is the best way to slurp in a file, process it, and just load it into your clojurescript without having it be re-processed each time?

ClashTheBunny04:07:57

I also asked on clojureverse, if people could possibly answer there and keep the solution for the ages: https://clojureverse.org/t/loading-a-csv-into-clojurescript-with-macros/2420

dpsutton04:07:18

have you seen this? @clashthebunny

ClashTheBunny04:07:55

I’ve had things like that working if I preprocess data into edn in a separate process, but I’d like to have the data in a csv and then have the macro both slurp and parse it.

thheller07:07:13

@clashthebunny dumping a CSV file into the code is a terrible idea and you should load if via ajax request instead.

thheller07:07:19

or just dump it as a string and parse on the client. depends on the size I guess.

ClashTheBunny07:07:16

So, you would make a program to parse and dump the CSV to edn, and then you would load that via ajax? Just write two “programs”?

thheller07:07:45

no just load and parse the csv on the client

Jp Soares10:07:52

Wouldn't frameworks be against the general philosophy of clojure? I understand that the philosophy is to have a simple and flexible language and grow it with libraries. Frameworks strict you more than libraries and then is difficult to use more than 1 framework. Using a framework like Re-Frame in clojurescript can be more composable than using a framework like Angular in javascript?

souenzzo11:07:34

I have a re-frame app and I'm moving to fulcro. ATM I have a re-frame event that works like transact, I use some fulcro functions to do the query->db->tree normalization but still on re-frame. I already tested that fulcro components can be used mixed with reagent. Both are pretty modular, can be used together and can be extended without problems.

4
souenzzo11:07:34

I have a re-frame app and I'm moving to fulcro. ATM I have a re-frame event that works like transact, I use some fulcro functions to do the query->db->tree normalization but still on re-frame. I already tested that fulcro components can be used mixed with reagent. Both are pretty modular, can be used together and can be extended without problems.

4
bhauman13:07:39

@clashthebunny if the csv file is static data that you are including on the page this is a fine technique

bhauman13:07:47

your problem is the doall is returning a list

bhauman13:07:18

so you are getting (["asdfasdf" "asd"] ["asdf" "asdf"])

bhauman13:07:31

and its using the vector as a function

bhauman13:07:44

which only takes one or two arguments

ClashTheBunny13:07:57

It does seem like something that would add to load time.

bhauman13:07:11

depends on the size of the file

ClashTheBunny13:07:13

That totally makes sense.

bhauman13:07:54

its returning code right? so you can fix it by (vec (doall ...

ClashTheBunny13:07:42

Yep, it's returning code and the JavaScript I pasted sure seems like it's calling the last list as a function.

bhauman13:07:43

this is the most common macro mistake

bhauman13:07:33

sequences become function calls when we are not expecting one

bhauman13:07:37

I make this mistake all the time, so when I see a wrong arity error or a is not a function error it is the first thing I look for

ClashTheBunny13:07:53

Yeay! I learned the syntax and many of the functions of Clojure, but I still feel like there are so many gotchas I haven't learned. The hidden power rears its head!

bhauman13:07:49

macros are super advanced 🙂

bhauman14:07:34

@ghopper the -co option without any other args right now is meaningless

Garrett Hopper14:07:20

That makes sense. Shouldn't it either throw an error or be ignored instead of what it's doing now?

Garrett Hopper14:07:50

It does make it behave differently though. See above ^

kennytilton14:07:27

@dfcarpenter stenciljs looks great, but when I look at the list of “wins” I do not see much that cljs and its web frameworks do not already supply. Just my take, though.

bhauman14:07:29

yes but it is undefined without a main option, it should it throw an error

bhauman14:07:55

@ghopper cljs.main needs a bunch more error checking

Garrett Hopper14:07:12

Ok đź‘Ť:skin-tone-2:

bhauman14:07:25

that doesn't mean its going to happen 🙂

bhauman14:07:46

and if you want a certain behavior you should always supply a main option -c -r etc, and if you want -co compile options to take effect you probably always want to use the -c option.

bhauman14:07:05

otherwise you are relying on the state of your compiled output

bhauman14:07:28

and this can get dicey

bhauman14:07:06

@ghopper have you tried figwheel.main yet?

bhauman14:07:07

it has a lot of error checking

Garrett Hopper14:07:26

Yeah, I'm using it during development. :) It's excellent. For actual builds I'm going with cljs.main to keep things simple and consistent.

bhauman14:07:00

fig.main works really well for builds too 🙂

Garrett Hopper14:07:20

I originally ran into these issues with figwheel, I just dropped down to cljs.main to make the bug reports.

bhauman14:07:31

OK well that makes sense

bhauman14:07:19

just checking that you are aware of the -bo option?

bhauman14:07:50

I'm currently hammocking a strategy to automate the webpack stuff

bhauman14:07:48

first just trying to figure out structure for configuration that is expressive enough to cover most of the cases

Garrett Hopper14:07:04

@bhauman Yeah, I am. 🙂 I didn't like having my builds in the root directory though, so I've been falling back to -co @$build.edn -c.

Garrett Hopper14:07:10

Oh, nice. That would be useful. 🙂

bhauman14:07:53

oh did the -bo $build-dir/$build-name not work?

Garrett Hopper14:07:49

Huh, I didn't try that. I just tried with the actual path including the .cljs.edn. That may work.

john16:07:52

@bhauman It's definitely not a normal usage of tagged-literals. I was basically looking for a way to conditionally do requires in certain namespaces, depending on project config, similar to how @thheller described here: https://dev.clojure.org/jira/browse/CLJS-2396

john16:07:19

yeah, I did see that you were doing that. I considered using that method

john16:07:59

So, basically, now I can just tuck a #mything/node before a particular require vector, which only returns the vector when some some closure-defines of some node keyword is present

đź‘Ś 4
john02:07:31

Actually, bad idea, since the nils fail spec for the ns form. This works though:

;; /co.edn
{:main "my.core"
 :watch "src"
 :output-dir "out"
 :output-to "out/main.js"
 :asset-path "/out"
 :optimizations :none
 :source-map true
 :browser-repl true
 :closure-defines {my.require/opts false}}

;; src/data_readers.cljc
{my/if-env my.impl/if-env}

;; src/my/impl.clj
(ns my.impl
  (:require [cljs.env :as env]))

(defn if-env [[if-str? then? else?]]
  (if (-> env/*compiler* deref :options :closure-defines (get if-str?))
    then?
    else?))

;; src/my/core.cljs
(ns my.core
  #my/if-env ["my.require.opts"
              (:require [cljs.pprint :as pp]
                        [cljs.reader :as reader])
              (:require [cljs.reader :as reader])])

(println "cljs.reader:" reader/read-string)
(println "cljs.pprint:" pp/pprint)
;=> Uncaught ReferenceError: pp is not defined

idiomancy16:07:26

hey, dumb question. Why is it that in react based cljs wrappers, you never specify that you're writing to the body tag? Is the virtual dom always expecting one form that will be used to render the <body> tag of the html doc?

thheller16:07:53

you always specify which element to render into. could be body typically is some div

thheller16:07:22

ie. reagent/render takes two args, I believe second is the element to render into

idiomancy16:07:07

gotcha, that makes sense.

tstephens16:07:02

rendering into the body might do funny things if you’re loading your script from a script tag in the body (which is recommended, for parse/load times): <body><script src="my-app.js"></script></body>

tstephens16:07:15

after you render your app into the body tag, the script tag loading the app would be removed by React

tstephens16:07:57

also, if you have some static content “around” your app (headers, footers, etc that don’t need to be dynamically controlled by the app code), you can just have it in the HTML and not waste cycles rendering it via react

idiomancy17:07:28

Huh, good insight, thanks @tstephens

lilactown17:07:20

anyone run devcards tests in CI?

sova-soars-the-sora17:07:45

idiomatic way to add event listener to fairly simple html/cljs page? wanting to use goog.events but not sure if i'm missing something in project.clj or what

Garrett Hopper17:07:01

Can :foreign-libs be used without :file defined? This seems to indicate that it can in order to use :global-exports: https://clojurescript.org/news/2017-07-30-global-exports.

Building client...
Exception in thread "main" java.lang.Exception: No :file provided for :foreign-libs spec {:provides ["google.api"], :global-exports {google.api gapi}, :foreign true}
        at cljs.js_deps$build_index$fn__1074.invoke(js_deps.cljc:183)

sova-soars-the-sora17:07:06

lol i had my javascript embedded in the head tag so it never saw the dom elements load... doi..

dnolen17:07:38

@ghopper only if you’re overriding

dnolen17:07:22

and that doesn’t really have anything to do with :global-exports per se, it’s just a useful feature

Garrett Hopper17:07:38

@dnolen Overriding from where? I'm not quite following.

Garrett Hopper17:07:28

Well, it would only make sense if you were using :global-exports. Otherwise, there's no point.

dnolen17:07:32

so other foreign dep entry with the same :provide

dnolen17:07:43

and that isn’t true

dnolen17:07:53

again there are other reasons to override

dnolen17:07:58

feature has existed for years

dnolen17:07:03

before global exports

Garrett Hopper17:07:04

So the example I linked to is no longer valid?

:foreign-libs [{:provides ["cljsjs.react"]
                :global-exports '{cljsjs.react React}}
               {:provides ["cljsjs.react.dom"]
                :global-exports '{cljsjs.react.dom ReactDOM}}]

dnolen17:07:25

I don’t know what you’re saying

dnolen17:07:29

or how I implied that

dnolen17:07:34

anyways I need to run for a bit

Garrett Hopper17:07:08

I want to :provides a namespace with a global export from the page. I'll provide the <script> on the page, and I want to use it from Clojurescript. Is there no way to do that, since I can't use :foreign-libs without a :file? It seems like that's what's being done here: https://clojurescript.org/news/2017-07-30-global-exports.

Garrett Hopper17:07:23

@souenzzo Oh, interesting. So I could just use a dummy :file for all my exports?

idiomancy17:07:45

grrr... can't get figwheel-sidecar css reloading... it claims the watcher exists/has been set up, but changes don't seem to send any messages!

...
Figwheel: Starting server at 
Figwheel: Watching build - dev
Compiling "resources/public/js/compiled/app.js" from ["src/cljs"]...
Successfully compiled "resources/public/js/compiled/app.js" in 8.368 seconds.
Figwheel: Starting CSS Watcher for paths  ["resources/public/css"]
Launching ClojureScript REPL for build: dev
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
...

idiomancy17:07:16

its clearly looking for changes in that directory

idiomancy17:07:59

is the link supposed to be autogenerated? I included it explicitly as <link href="css/style.css" rel="stylesheet" type="text/css">

idiomancy17:07:48

there must be something I'm missing. The default figwheel sidecar system definitely includes a CSS watcher component...

dnolen18:07:08

@ghopper now that you’ve explained what you are trying to do I understand and yes you misunderstood what was being shown before

dnolen18:07:21

:file was always a requirement

dnolen18:07:36

you were (admittedly non-obvious) confused by the override capability

Garrett Hopper18:07:14

Sorry it wasn't clear at first. I'm still not seeing how the example I linked to is overriding anything.

dnolen18:07:39

something else is providing that ns

dnolen18:07:46

what you want isn’t supported

dnolen18:07:07

but … it probably wouldn’t be hard to do and is an interesting enhancement

Garrett Hopper18:07:24

Provided in the same way :foreign-libs provides the namespace, or just that the namespace exists elsewhere?

Garrett Hopper18:07:31

Ok, thanks :thumbsup: 🙂

dnolen18:07:47

somebody else defined the foreign lib

dnolen18:07:52

they are indexed on :provides

Garrett Hopper18:07:54

Gotcha :thumbsup:

dnolen18:07:05

feel free to file an enhancement ticket in JIRA

dnolen18:07:30

:foreign-lib w/o :file, motivation being support :global-exports on external libs which are not part of any build

idiomancy18:07:47

nah, nothing yet. the tooling battle rages on

bhauman18:07:05

so basically what is your set up?

idiomancy18:07:32

re-frame/intellij+cursive/figwheel-sidecar

bhauman18:07:38

you can set your :build {:figwheel {:debug true}}

4
idiomancy18:07:40

in project clj:

:figwheel {:css-dirs ["resources/public/css"]
             :ring-handler understanding-re-frame.handler/dev-handler}
in index.html:
<head>
    <meta charset='utf-8'>

    <link href="css/style.css" rel="stylesheet" type="text/css">
  </head>
repl startup command:
(use 'figwheel-sidecar.repl-api)
(start-figwheel!) ;; <-- fetches configuration
(cljs-repl)

bhauman18:07:01

yeah that looks good

bhauman18:07:54

:debug is in the builds :figwheel key

idiomancy18:07:06

trying it now. is it handy?

bhauman18:07:34

then when clean and recompile, you look in the devtools console to see the messages coming from figwheel

bhauman18:07:31

you can also look at the css link element in devtools to see if its changing

bhauman18:07:56

It may be changing but the CSS rule isn't taking for some other reason

idiomancy18:07:01

oh weird... its... its working now? But only after I have made some change to the dom...

Garrett Hopper18:07:07

@dnolen Thanks again 🙂 I've created https://dev.clojure.org/jira/browse/CLJS-2810 I was mainly just trying it to work around https://dev.clojure.org/jira/browse/CLJS-2809, so I guess I'll have to find something else.

idiomancy18:07:33

huh, thanks @bhauman that's a really good tool. I can see the order of operations here this way.

idiomancy18:07:11

still not totally sure why its working now tbh 🤪

dnolen18:07:48

@ghopper hrm 2809 just seems like a bug around handling urls not order

dnolen18:07:00

why not just download that file?

Garrett Hopper18:07:24

Well, doing the requires in the other order fixes the problem. I'm not sure why requiring a local :npm-dep would break the requiring of a remote file. You've got point though. I could just use a local file for now.

Garrett Hopper18:07:55

I may have made a bad assumption. When a remote file is used in :foreign-libs, is it downloaded at compile time or runtime?

dnolen18:07:18

compile time I believe, it’s probably just a simple bug

Garrett Hopper18:07:21

In that case, I shouldn't be using this at all, since I need to always have the latest of the library in case the APIs change. Yeah, I'm sure it's something simple. In any case, I suppose I don't need to worry about it after all. 🙂

john02:07:31

Actually, bad idea, since the nils fail spec for the ns form. This works though:

;; /co.edn
{:main "my.core"
 :watch "src"
 :output-dir "out"
 :output-to "out/main.js"
 :asset-path "/out"
 :optimizations :none
 :source-map true
 :browser-repl true
 :closure-defines {my.require/opts false}}

;; src/data_readers.cljc
{my/if-env my.impl/if-env}

;; src/my/impl.clj
(ns my.impl
  (:require [cljs.env :as env]))

(defn if-env [[if-str? then? else?]]
  (if (-> env/*compiler* deref :options :closure-defines (get if-str?))
    then?
    else?))

;; src/my/core.cljs
(ns my.core
  #my/if-env ["my.require.opts"
              (:require [cljs.pprint :as pp]
                        [cljs.reader :as reader])
              (:require [cljs.reader :as reader])])

(println "cljs.reader:" reader/read-string)
(println "cljs.pprint:" pp/pprint)
;=> Uncaught ReferenceError: pp is not defined