Fork me on GitHub
#shadow-cljs
<
2022-11-17
>
peterdee01:11:45

I loaded an npm module that I made myself today (I'm not a javascript programmer) and shadow didn't complain. But when I try to call one of its functions (nb/base64encode "abc"), from a REPL I get module$node_modules$nata_borrowed$nataBorrowed.base64encode is not a function I'm really impressed that I got that far on the first shot! It knows where to find the function, but it says it isn't a function. Maybe this is a clojurescript question. How do I call a function? I see lots of example of calling methods.

thheller06:11:54

@peterd how did you write the JS? did you export the function properly? if its just a named function in the JS file that is not enough, it needs to be exported

peterdee16:11:30

@thheller: I put the code in a function that does as shown below (The code can be found here https://github.com/pdenno/nata-borrowed/blob/main/src/nataBorrowed.js) Thanks for the pointer on native encode/decode. There are more functions that I'll need so I'll need to know how to do this anyway. //Top of the file looks like this var utils = require('./utils'); var dateTime = require('./datetime'); const nataBorrowed = (() => { 'use strict'; // Then the function definition // Bottom of file... return { base64encode }; })(); module.exports = nataBorrowed;

peterdee17:11:07

Here is a simpler example. Following https://www.sitepoint.com/understanding-module-exports-exports-node-js/, I created const getName = () => { 'use strict'; // Linting wanted this line. return 'Jim'; }; exports.getName = getName; Then I did npm publish --dry-run and copied the results and package.json into a directory that I placed in my program's node_modules directory. It contained: -rw-rw-r-- 1 pdenno pdenno 2009 Nov 17 11:51 package.json -rw-rw-r-- 1 pdenno pdenno 1005 Nov 17 11:55 smallExample-es5.js -rw-rw-r-- 1 pdenno pdenno 829 Nov 17 11:55 smallExample-es5.min.js -rw-rw-r-- 1 pdenno pdenno 0 Nov 17 11:55 smallExample.js -rw-rw-r-- 1 pdenno pdenno 527 Nov 17 11:55 smallExample.min.js I added smallexample 1.0.0 to my program's package.json and fired up shadow. npx shadow-cljs -d nrepl/nrepl:1.0.0 -d cider/piggieback:0.5.3 -d cider/cider-nrepl:0.28.5 watch kaocha-test Connecting with cider-connect-cljs, in the REPL: (se/getName) Execution error (TypeError) at (<cljs repl>:1). module$node_modules$smallexample$smallExample.getName is not a function I suspect that I just don't know how to call the function.

thheller17:11:45

so how do you actually require and call the functions?

thheller17:11:15

and which shadow-cljs version do you use? if you do all this from the REPL there were some issues a while ago

peterdee21:11:07

(ns rad-mapper.builtin (:require ... #?(:cljs ["smallexample" :as se]))) Then I'm just calling it with (se/getName) for example. I am using shadow-cljs 2.20.5. It could be something really stupid. This is all pretty new to me.

thheller04:11:45

that would be fine

thheller04:11:53

but there is so much more that could be wrong

thheller04:11:12

like what is your build config? how are you running all this?

thheller04:11:37

why are you making it a npm package in the first place?

peterdee17:11:00

To your last two questions: 1. My build config is the :kaocha-test target in this https://github.com/pdenno/RADmapper/blob/main/shadow-cljs.edn 2. I thought that npm would be the natural choice for shadow-cljs, and also the code I'm integrating, from a tool called JSONata, is provided as an npm package. (I have tried loading the entire JSONata npm package, but get the same results.)

thheller07:11:21

so I compiled your project and just added a require for ["jsonata" :as ja] and then added (js/console.log "ja" ja) and it appears to be all fine? I can't test your other package locally? or is it somewhere in that repo?

peterdee19:11:30

Well, I think I did warn you that it might be something stupid! My build process created the files shown in the message above, which, as shown, included -rw-rw-r-- 1 pdenno pdenno 0 Nov 17 11:55 smallExample.js which I did not notice is a file of size of 0 bytes. For anyone reading this thread, there were a few useful thing I learned in the process: Most significantly, I didn't realize that, for example, (require '["jsonata" :as ja]) binds ja to the object that you export from the npm module. You can just evaluate ja in the REPL and see the object. The object bound to the empty npm module smallExample.js is #js {}. That's good to know for debugging. Secondly, you might have to explicitly do the require in the REPL. Just being in a namespace that requires it might not be enough. (Or perhaps I have another bug to chase down.) Thanks for the help! Sorry for the noise. shadow-cljs is a great tool.

thheller20:11:33

if you just (in-ns 'your.ns) in the REPL before having loaded it the namespace will indeed be empty and contain no requires

thheller20:11:46

to load it properly you must (require 'your.ns) first

thheller20:11:54

or eval the whole ns form in the REPL

thheller20:11:04

just switching is not enough. common mistake.

thheller06:11:37

also note that there are base64 related functions available in the closure library. (:require [goog.crypt.base64 :as b64]) then (b64/decodeString ...)

peterdee17:11:10

Thanks! That works!

mokr08:11:01

Hi, with shadow-cljs v2.20.11 I still see the recent core.async java.lang.NoSuchFieldError: __thunk__0__ issue commented on in commit https://github.com/thheller/shadow-cljs/commit/b034b24d8d20e538afe31b6a19087f021d0813e2 After quite a bit of fault searching and trial-and-error. I’ve come to the point where a suggestion or two would be nice. Some details: • My project is based on the https://luminusweb.com. • All was good until shadow-cljs started using core.async v1.6.673 • The issue goes away if I downgrade to core.async v1.5.648 • My dependencies list all versions of clojure, clojurescript, clojurescript, closure-compiler-unshaded and core.async that shadow-cljs requires according to clojars and error message. I also tried moving all of them to :managed-dependencies in project.clj, without noticeable effect. • As a test I started out with a clean lein new luminus myproject +shadow-cljs and bumped shadow-cljs to v2.20.11 and all mentioned libs to the versions shadow requires, it still worked. I then started adding other libs my project uses and error reappeared when I added [com.wsscode/pathom3 "2022.10.19-alpha"] as a dependency and required it as [com.wsscode.pathom3.connect.operation :as pco] in myproject/core.clj. I noticed that Pathom3 also uses shadow-cljs. It’s honestly not that clear to me which library is to “blame” here. Seems like a snowballing effects starting with a change to core.async, but this is all a bit deeper than where I usually spend my time. Staying at core.async v1.5.648 might be a workaround for now, but does not seem like a good option in the long run. Other suggestions would be very much appreciated.

thheller08:11:50

hmm yeah thats not great. for me it seemed fixed after that commit

thheller08:11:11

do you do any AOT yourself?

thheller08:11:47

pathom using shadow-cljs doesn't matter. since that is not part of the deployed jar

thheller08:11:17

the blame is core.async I guess, not exactly sure why this is all even a problem but AOT is weird that way

shawn08:11:58

is there a way to be less aggressive about compiling this:

(def query (js-template graphql "
query reagentProject_pages_homeQuery {
                            rates(currency: \"USD\") {
                              currency
                              rate
                            }
                           }
"))
...
  (let [{:keys [rates]} (-> useLazyLoadQuery query)]
such that instead of getting this:
reagent_project.pages.home.query = module$node_modules$react_relay$index.graphql`
query reagentProject_pages_homeQuery {
                            rates(currency: "USD") {
                              currency
                              rate
                            }
                           }
`;
var map__42409 = (reagent_project.pages.home.query.cljs$core$IFn$_invoke$arity$1 ? reagent_project.pages.home.query.cljs$core$IFn$_invoke$arity$1(module$node_modules$react_relay$index.useLazyLoadQuery) : reagent_project.pages.home.query.call(null,module$node_modules$react_relay$index.useLazyLoadQuery));
I can get this:
var query = graphql`
query reagentProject_pages_homeQuery {
                            rates(currency: "USD") {
                              currency
                              rate
                            }
                           }
`;
var data = useLazyLoadQuery(query, {});
I had tried to update my shadow-cljs.edn like so:
:builds       {:app {:target     :esm
                      :compiler-options {:optimizations :simple}
                      :output-dir "resources/public/js"
                      :asset-path "/js"
                      :modules    {:app {:init-fn reagent-project.core/init!}}}}
 :dev-http     {3000 {:root    "resources/public"
                      :handler reagent-project.handler/app}}}

thheller08:11:27

where does useLazyLoadQuery come from?

thheller08:11:04

(-> useLazyLoadQuery query) why the ->?

thheller08:11:16

just (useLazyLoadQuery query) should generate the proper code?

thheller08:11:46

but all depends on what useLazyLoadQuery is

thheller08:11:59

thats not what I meant. where does it come from in your code

thheller08:11:11

is it a :refer or how did you declare it?

shawn08:11:20

the complete component is:

(defnc home []
  (let [{:keys [rates]} (-> useLazyLoadQuery query)]
    (let [{:keys [currency rate]} rates]
      (d/div {:key currency}
             (d/p currency ": " rate)))))
    ;; (d/span {:class "main"}
    ;;  (d/h1 "Welcome to reagent-project")
    ;;  (d/ul
    ;;   (d/li (d/a {:href (path-for :items)} "Items of reagent-project"))
    ;;   (d/li (d/a {:href "/broken/link"} "Broken link"))))
  ;; )

(defn home-page []
  (helix.core/suspense
   {:fallback "loading..."}
   ($ home)))

thheller08:11:43

I need to see the definition of useLazyLoadQuery

thheller08:11:06

what is that? is it from a :require in your ns? is it a local def?

shawn08:11:07

(ns reagent-project.pages.home
  (:require
   [reagent-project.utils.path-for :refer [path-for]]
   ["react-relay" :refer [useLazyLoadQuery graphql]]
   [helix.core :refer [defnc $]]
   [shadow.cljs.modern :refer [js-template]]
   [helix.dom :as d]
   [reagent.core :as reagent :refer [atom]]))

(def query (js-template graphql "
query reagentProject_pages_homeQuery {
                            rates(currency: \"USD\") {
                              currency
                              rate
                            }
                           }
"))


(defnc home []
  (let [{:keys [rates]} (-> useLazyLoadQuery query)]
    (let [{:keys [currency rate]} rates]
      (d/div {:key currency}
             (d/p currency ": " rate)))))

(defn home-page []
  (helix.core/suspense
   {:fallback "loading..."}
   ($ home)))

thheller08:11:21

ok then remove the ->?

thheller08:11:12

why does it matter though?

thheller08:11:35

I mean the likelyhood of some other compiler recognizing the CLJS output is very slim

shawn08:11:58

ah, I see. I was considering using a vite transform plugin with this. but I think I'll use the babel one instead to complete the transform

thheller08:11:44

so part of the weird code you are seeing is the destructure

thheller08:11:24

(defnc home []
  (let [result (useLazyLoadQuery query)
        {:keys [rates]} result
        {:keys [currency rate]} rates]
    (d/div {:key currency}
      (d/p currency ": " rate))))

thheller08:11:33

might generate code that babel may "accept"

thheller08:11:44

but I wouldn't hold my breath to be honest 😛

shawn08:11:03

nice. thanks again! I'll try that out.

mokr08:11:45

@thheller Honestly AOT is not something I think about. Luminus comes with :main ^:skip-aot myproject.core, but I do not active use AOT myself.

thheller08:11:05

I just release 2.20.12 with the downgraded core.async

mokr08:11:30

In addition the :uberjar profile in project.clj has :aot :all

mokr08:11:51

Thanks, I’ll upgrade shadow-clj