Fork me on GitHub
#shadow-cljs
<
2022-02-09
>
berkeleytrue04:02:52

Is anyone using shadow-cljs with some sort of task runner? I was hoping to use one command to run the build/watch and then run my nodescript outputted by shadow-cljs. I'd normally do this with GulpJS. Are people using tools.deps to do this?

berkeleytrue04:02:40

I have a two part project (server and cli), it would be cool to run the build for both in parallel and start cli/server files so I can connect to their js runtimes for repl use.

berkeleytrue16:02:40

Thanks. I haven't heard of it. Seems like I wouldn't be able to get the outcome I wanted for target node-scripts since I need to wait until the build completes before running the script. That is a good workflow for a browser app, though.

p-himik09:02:38

What could be the cause of a situation where shadow-cljs does not complain about a missing ^js but that tag not being present still results in an incorrectly minified name? My guess is that name was used in e.g. Closure library. Are there any other potential causes?

thheller09:02:41

don't know. what kind of form is it?

thheller09:02:03

(set! *warn-on-infer* false) turns everything off, just in case that is in the file?

p-himik09:02:08

No, I never use that set!. Here's the code, with some crud removed:

(defn replace-track-source! [^js playlist track-name src]
  (loop [tracks (.-tracks playlist)]
    (when-first [track tracks]
      (if (= (.-name track) track-name)
        (do
          (-> (to-audio-buffer src playlist)
              (.then (fn [audio-buffer]
                       (set! track -src audio-buffer)
                       (.setBuffer track audio-buffer)))))
        (recur (next tracks))))))
:advanced renamed .setBuffer.

thheller10:02:27

hmm dunno. it doesn't warn about anything that already has externs, setBuffer seems like it may exist

thheller10:02:34

you sure its actually that thing getting renamed? I mean actually confirmed with :pseudo-names?

p-himik10:02:35

Yep, did a release build with --debug - it became .$setBuffer$. Adding ^js in front of track in the when-first form fixes that.

p-himik10:02:15

Can you give me some pointers on how I should debug this in shadow-cljs?

thheller10:02:41

in the .shadow-cljs/builds/app/release dir should be a externs txt file

thheller10:02:49

that includes all the externs generated for the build

thheller10:02:03

first confirm setBuffer is actually not in there

thheller10:02:43

then you can use

:build-hooks
   [(shadow.build/tap-hook)]
in your build config

thheller10:02:56

that will only send a tap to the inspect UI

thheller10:02:34

that'll be the entire build-state, so you can check the externs there too

thheller10:02:03

:compiler-env -> :cljs.analyzer/namespaces -> your.ns -> :shadow/js-* should contain it

thheller10:02:32

externs inference happens mostly in the cljs compiler. so maybe try with the regular CLJS compiler too

p-himik10:02:07

Yep, it's neither in the externs file nor in the inspect UI. Some other fields are also not in the inspect UI - I suspect because they were first picked up in some other ns? E.g. .then is not there, even though it definitely works, but .resolve is there.

thheller10:02:49

there are other places where externs are stored

thheller10:02:17

.then is definitely in the default externs

p-himik10:02:19

But not .resolve? Huh.

thheller10:02:59

resolve is usually Promise.resolve so why would there be a generic .resolve?

thheller10:02:07

at least I can't think of a generic .resolve?

thheller10:02:38

if closure knows the type of something then is can be more specific with externs

p-himik10:02:31

My understanding of externs is very limited indeed. :) I see. Anyway, I'll try to stumble around with a proper debugger and see what I can find.

p-himik12:02:58

Seems like indeed a CLJS issue - the tags returned for track by the analyzer are any and clj-nil.

p-himik12:02:55

Ah, crap. Pretty sure I figured it out. Notice that when-first - it gets expanded into essentially (let [track (first (seq tracks))] ...). And seems like that seq makes CLJS analyzer forget about the ^js tag.

Simon13:02:12

When setting :compiler-options {:externs ["/app/components/user/payment.js"] i get this:

[:app] Compiling ...
[BABEL] Note: The code generator has deoptimised the styling of /Users/simonchristensen/Documents/Developer/movenation/calculator/node_modules/@firebase/firestore/dist/index.esm2017.js as it exceeds the max of 500KB.
{:type :missing-externs, :extern "/app/components/user/payment.js", :shadow.build.log/level :warn}
{:type :missing-externs, :extern "/app/components/user/payment.js", :shadow.build.log/level :warn}
[:app] Build completed. (3727 files, 3616 compiled, 0 warnings, 95.37s)

Simon13:02:20

fixed by removing / prefix: :compiler-options {:externs ["app/components/user/payment.js"]

Simon13:02:44

But now this file is not supported since, the js version used is too new:

JSC_LANGUAGE_FEATURE. This language feature is only supported for ECMASCRIPT_2015 mode or better: modules. at EXTERNS:app/components/user/payment.js line 1 : 0
JSC_LANGUAGE_FEATURE. This language feature is only supported for ECMASCRIPT_2015 mode or better: modules. at EXTERNS:app/components/user/payment.js line 2 : 0
JSC_LANGUAGE_FEATURE. This language feature is only supported for ECMASCRIPT_2015 mode or better: const declaration. at EXTERNS:app/components/user/payment.js line 5 : 0
JSC_LANGUAGE_FEATURE. This language feature is only supported for ECMASCRIPT_2015 mode or better: object destructuring. at EXTERNS:app/components/user/payment.js line 5 : 6
JSC_LANGUAGE_FEATURE. This language feature is only supported for ECMASCRIPT_2015 mode or better: const declaration. at EXTERNS:app/components/user/payment.js line 6 : 0
JSC_LANGUAGE_FEATURE. This language feature is only supported for ECMASCRIPT_2015 mode or better: const declaration. at EXTERNS:app/components/user/payment.js line 7 : 0
JSC_LANGUAGE_FEATURE. This language feature is only supported for ECMASCRIPT_2015 mode or better: modules. at EXTERNS:app/components/user/payment.js line 9 : 0

thheller16:02:26

what does that mean js version used is too new? there is no point in using new features in externs?

thheller16:02:37

you can't just feed it the normal code? that is not what externs are.

isak18:02:59

For the :browser-test target, is there any way to influence the path it uses in the index.html file? <script src="/js/test.js"> (I mean the /js/test.js there, I'd like to change that path) I tried setting :asset-path, like one would for the :browser target, but that was ignored.

thheller18:02:09

should be fine? I mean changing it in the html + asset-path?

Christopher Genovese18:02:41

I've got a library with the :node-library target that has one basic interface point between the clojurescript and javascript worlds. I use a deftype with Object methods to provide the javascript-side interface, e.g.,

(deftype Mediator
  Object
  (opA [this] ...)
  (opB [this in] ...)
  ...)
When done this way, I get the Cannot infer target type warnings on the deftype expression. I set :infer-externs :auto in compiler options, which does not fix the warnings. The warnings do go away when instead when I do deftype ^js Mediator, but that turns the Mediator type into a POJO requiring js-style operations on the object inside the methods (e.g., set! works but not assoc). This is fine, and I can live with it. I am curious, however, about best practices for this use case. What is the best way to create a javascript object interface to clojurescript code with shadow-cljs? Will the javascript methods (e.g., opA and opB ) be externed properly this way under advanced compilation? And is it necessary to make the mediating type a mutable POJO or is it possible to have clojurescript data that is treated as an opaque object on the javascript side? I'd appreciate hearing what other people do in this situation. Thanks!

thheller19:02:49

this really is hard to answer if you take out the part generating inference warnings?

thheller19:02:54

the above snippet should be fine?

Christopher Genovese19:02:38

@thheller (assuming you are answering me, please disregard if not) Do you mean that the first deftype (without ^js) should not generate inference warnings?

Christopher Genovese19:02:26

I would add that the inference warnings are still emitted even if I treat the this as a POJO in the methods.

isak19:02:31

@thheller oh, I thought it would get overwritten constantly. Sounds good.

thheller20:02:13

@genovese you have given only partial of the code and the error message so I do not know what you are talking about. you get an inference warning on what exactly?

thheller20:02:58

what does "deftype ^js Mediator, but that turns the Mediator type into a POJO requiring js-style operations on the object" mean? I do not understand what that means?

thheller20:02:14

do you mean deftype vs defrecord?

thheller20:02:07

deftype with Object methods are completely fine but without knowing more about what exactly you are doing its hard to give a accurate answer

Christopher Genovese20:02:52

Sorry. First, here's an example of the message:

26 | (deftype Mediator [frame options env]
-------^------------------------------------------------------------------------
 Cannot infer target type in expression (. (. MathLingo -prototype) -render)
--------------------------------------------------------------------------------
  27 |   Object
  28 |   (render [this]
  29 |     (str "TBD" (process frame))))
With ^js, there inference error goes away. On the second point, you're right, my mistake. I did a deftype here, not a defrecord. I had done it earlier with defrecord and then changed it with the type hint but foolishly ignored that (even as I typed it), so of course assoc no longer worked. Apologies. The remaining questions are why is the ^js needed, and is there a better way to achieve this goal than what I'm doing here. Thanks!

thheller20:02:56

uhm I'm confused. deftype Mediator but (. MathLingo -prototype)?

Christopher Genovese20:02:53

Sorry, name changed. Should be (. Mediator -prototype). Not really on my game today 🙂

thheller20:02:40

maybe you are on an old shadow-cljs or clojurescript version?

Christopher Genovese20:02:01

Hmm...not sure what to make of that. Here's my config

{:source-paths
 ["src/dev"
  "src/main"
  "src/test"]

 :dependencies
 [[better-cond             "2.1.0"]
  [binaryage/oops          "0.7.0"]
  [camel-snake-kebab       "0.4.2"]
  [data.deque              "0.1.0"]
  [instaparse              "1.4.10"]
  [metosin/potpuri         "0.5.3"]
  [com.rpl/specter         "1.1.3"]
  [net.cgrand/xforms       "0.19.2"]
  [org.clojure/algo.monads "0.1.6"]
  [org.clojure/core.match  "1.0.0"]
  [org.clojure/test.check  "1.1.1"]]

 :builds
 {:main {:target :node-library
         :output-to "out/mathlingo.js"
         :exports {:createParser mathlingo.core/create-parser}
         :compiler-options {:infer-externs :auto}
         :devtools {:repl-pprint true}}
  :test {:target    :node-test
         :output-to "out/node-tests.js"
         :ns-regexp "-test$"
         :autorun   false}}

 :nrepl {:port 8777}

 :open-file-command ["emacsclient" "-n" ["+%s:%s" :line :column] :file]}

thheller20:02:03

(deftype Mediator [frame options env]
  Object
  (render [this]
    "TBD"))

thheller20:02:08

this is fine and produces no warnings for me

Christopher Genovese20:02:08

It does not produce warnings for me at the repl, but when it compiles it does.

thheller20:02:16

I didn't try at the REPL

thheller20:02:56

which version of shadow-cljs? see npx shadow-cljs info

Christopher Genovese20:02:25

2.6.12 What could I be doing differently? I can try it in a raw project, but I don't see what I can tweak otherwise.

Christopher Genovese20:02:40

=== Version
jar:            2.16.12
cli:            2.16.12
deps:           1.3.2
config-version: 2.16.12

=== Paths
cli:     /.../node_modules/shadow-cljs/cli/dist.js
config:  /.../shadow-cljs.edn
project: /...
cache:   .shadow-cljs

=== Java
openjdk version "15.0.1" 2020-10-20
OpenJDK Runtime Environment (build 15.0.1+9-18)
OpenJDK 64-Bit Server VM (build 15.0.1+9-18, mixed mode, sharing)

thheller20:02:42

I don't know either. there is nothing to tweak. If you have a reproducible case I'm happy to take a look but so far this all looks fine

Christopher Genovese20:02:33

OK, thanks. I'll clear the cache and start again. And I'll assume all is ok. I appreciate the help.

thheller20:02:14

you can always turn off inference warnings by (set! *warn-on-infer* false) before the deftype or so and turn it back on again after

thheller20:02:34

I do remember that I had a case where I had to do that myself

thheller20:02:40

but I can't remember any details

Christopher Genovese20:02:40

Great, that will be more pleasant if it continues.