Fork me on GitHub
#shadow-cljs
<
2022-06-11
>
Vincent Cantin10:06:49

In version 2.19.1 , shadow-cljs emits warning about a few things which are running at macro time. For example, this:

;; source code at 

------ WARNING #1 - :undeclared-var --------------------------------------------
 Resource: cljs/analyzer.cljc:4403:12
--------------------------------------------------------------------------------
4400 |   []
4401 |   (try
4402 |     @(ns-resolve 'clojure.tools.reader '*alias-map*)
4403 |     (catch Throwable t
------------------^-------------------------------------------------------------
 Use of undeclared Var cljs.analyzer/Throwable
--------------------------------------------------------------------------------
4404 |       nil)))
4405 | 
4406 | #?(:clj
4407 |    (defn forms-seq*
--------------------------------------------------------------------------------

thheller10:06:19

@vincent.cantin this is a bug in CLJS itself. it has been fixed in master but there has been no release for that yet.

Vincent Cantin10:06:50

I see. It makes sense 🙂

Vincent Cantin10:06:39

Another (small) issue I found using shadow-cljs 2.19.1. The macro-expension doesn't recognize the edn alias :

------ ERROR -------------------------------------------------------------------
 File: [redacted]/config.cljc:5:1
--------------------------------------------------------------------------------
   2 |   (:require [ :as mio]
   3 |             #?(:clj [clojure.edn :as edn])))
   4 | 
   5 | (mio/inline-resource "endpoints.edn" edn/read-string)
-------^------------------------------------------------------------------------
Encountered error when macroexpanding .
Error in phase :compile-syntax-check
RuntimeException: No such namespace: edn
        clojure.lang.Util.runtimeException (Util.java:221)
        clojure.lang.Compiler.resolveIn (Compiler.java:7401)
        clojure.lang.Compiler.resolve (Compiler.java:7375)
        clojure.lang.Compiler.analyzeSymbol (Compiler.java:7336)
        clojure.lang.Compiler.analyze (Compiler.java:6785)
        clojure.lang.Compiler.analyze (Compiler.java:6762)
        clojure.lang.Compiler.eval (Compiler.java:7198)
        clojure.lang.Compiler.eval (Compiler.java:7149)
        clojure.core/eval (core.clj:3215)
        clojure.core/eval (core.clj:3211)
         (io.cljc:51)
         (io.cljc:42)
        clojure.core/apply (core.clj:671)
        clojure.core/apply (core.clj:662)
        cljs.analyzer/macroexpand-1*/fn--3702 (analyzer.cljc:4014)
mio/inline-resource is defined at https://github.com/green-coder/mate/blob/7107664b5e95521e8755e13b0f9cf19227408e24/src/mate/io.cljc#L51

Vincent Cantin10:06:35

A workaround is simply to write the fully qualified name clojure.edn/read-string.

thheller10:06:46

sorry thats just wrong 😛

Vincent Cantin10:06:28

ah 😅 I still don't understand the detail which I missed.

thheller10:06:59

well CLJC can be tricky that way

thheller10:06:18

you are basically writing two namespaces in one file

Vincent Cantin10:06:56

oh ... so you mean that the calling side of the macro is in the .cljs namespace ?

thheller10:06:58

and you are using eval which is extra tricky 😛

thheller10:06:05

also probably better to just inline the string and read it client side

👍 1
thheller10:06:09

certainly much smaller code size

Vincent Cantin10:06:05

I wasn't sure if my code was correct, but it does pass the tests with the same pattern via kaocha https://github.com/green-coder/mate/blob/made-in-taiwan/test/mate/io_test.cljc#L22

thheller10:06:30

I honestly don't know what the exact problem is. I think eval is just probably running in the wrong namespace context. meaning *ns* is not the one that has the edn alias

Vincent Cantin10:06:40

Thanks for your feedback. I will try to investigate.

Vincent Cantin11:06:06

I think/guess that the problem is simply that the symbol resolution is happening in the cljs namespace, way before the macro and its eval are called.

thheller11:06:02

no, the stacktrace is clojure only

thheller11:06:14

just log *ns* somewhere and see

thheller11:06:47

(tap> *ns*) or so

Vincent Cantin11:06:32

Full stacktrace:

2 |   (:require [ :as mio]
   3 |             #?(:clj [clojure.edn :as edn])))
   4 | 
   5 | (mio/inline-resource "endpoints.edn" edn/read-string)
-------^------------------------------------------------------------------------
Encountered error when macroexpanding .
Error in phase :compile-syntax-check
RuntimeException: No such namespace: edn
        clojure.lang.Util.runtimeException (Util.java:221)
        clojure.lang.Compiler.resolveIn (Compiler.java:7401)
        clojure.lang.Compiler.resolve (Compiler.java:7375)
        clojure.lang.Compiler.analyzeSymbol (Compiler.java:7336)
        clojure.lang.Compiler.analyze (Compiler.java:6785)
        clojure.lang.Compiler.analyze (Compiler.java:6762)
        clojure.lang.Compiler.eval (Compiler.java:7198)
        clojure.lang.Compiler.eval (Compiler.java:7149)
        clojure.core/eval (core.clj:3215)
        clojure.core/eval (core.clj:3211)
         (io.cljc:51)
         (io.cljc:42)
        clojure.core/apply (core.clj:671)
        clojure.core/apply (core.clj:662)
        cljs.analyzer/macroexpand-1*/fn--3702 (analyzer.cljc:4014)
        cljs.analyzer/macroexpand-1* (analyzer.cljc:4012)
        cljs.analyzer/macroexpand-1* (analyzer.cljc:3999)
        cljs.analyzer/macroexpand-1 (analyzer.cljc:4063)
        cljs.analyzer/macroexpand-1 (analyzer.cljc:4059)
        cljs.analyzer/analyze-seq (analyzer.cljc:4096)
        cljs.analyzer/analyze-seq (analyzer.cljc:4076)
        cljs.analyzer/analyze-form (analyzer.cljc:4285)
        cljs.analyzer/analyze-form (analyzer.cljc:4282)
        cljs.analyzer/analyze* (analyzer.cljc:4338)
        cljs.analyzer/analyze* (analyzer.cljc:4330)
        shadow.build.compiler/analyze/fn--15107 (compiler.clj:264)
        shadow.build.compiler/analyze (compiler.clj:252)
        shadow.build.compiler/analyze (compiler.clj:211)
        shadow.build.compiler/analyze (compiler.clj:213)
        shadow.build.compiler/analyze (compiler.clj:211)
        shadow.build.compiler/default-analyze-cljs (compiler.clj:408)
        shadow.build.compiler/default-analyze-cljs (compiler.clj:397)
        clojure.core/partial/fn--5908 (core.clj:2642)
        shadow.build.compiler/do-analyze-cljs-string (compiler.clj:318)
        shadow.build.compiler/do-analyze-cljs-string (compiler.clj:278)
        shadow.build.compiler/analyze-cljs-string/fn--15201 (compiler.clj:511)
        shadow.build.compiler/analyze-cljs-string (compiler.clj:510)
        shadow.build.compiler/analyze-cljs-string (compiler.clj:508)
        shadow.build.compiler/do-compile-cljs-resource/fn--15229 (compiler.clj:626)
        shadow.build.compiler/do-compile-cljs-resource (compiler.clj:607)
        shadow.build.compiler/do-compile-cljs-resource (compiler.clj:565)
        shadow.build.compiler/maybe-compile-cljs/fn--15332 (compiler.clj:958)
        shadow.build.compiler/maybe-compile-cljs (compiler.clj:957)
        shadow.build.compiler/maybe-compile-cljs (compiler.clj:933)
        shadow.build.compiler/par-compile-one (compiler.clj:1066)
        shadow.build.compiler/par-compile-one (compiler.clj:1021)
        shadow.build.compiler/par-compile-cljs-sources/fn--15368/iter--15390--15394/fn--15395/fn--15396/fn--15397 (compiler.clj:1139)
        clojure.core/apply (core.clj:667)
        clojure.core/with-bindings* (core.clj:1990)
        clojure.core/with-bindings* (core.clj:1990)
        clojure.core/apply (core.clj:671)
        clojure.core/bound-fn*/fn--5818 (core.clj:2020)
        java.util.concurrent.FutureTask.run (FutureTask.java:264)
        java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1136)
        java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:635)
        java.lang.Thread.run (Thread.java:833)

thheller11:06:48

RuntimeException: No such namespace: edn
        clojure.lang.Util.runtimeException (Util.java:221)
        clojure.lang.Compiler.resolveIn (Compiler.java:7401)
        clojure.lang.Compiler.resolve (Compiler.java:7375)
        clojure.lang.Compiler.analyzeSymbol (Compiler.java:7336)
        clojure.lang.Compiler.analyze (Compiler.java:6785)
        clojure.lang.Compiler.analyze (Compiler.java:6762)
        clojure.lang.Compiler.eval (Compiler.java:7198)
        clojure.lang.Compiler.eval (Compiler.java:7149)
        clojure.core/eval (core.clj:3215)
        clojure.core/eval (core.clj:3211)
         (io.cljc:51)

thheller11:06:53

this is the relevant part though

thheller11:06:10

the rest is just regular CLJS compilation stuff

Vincent Cantin11:06:54

(tap> *ns*) gives me the namespace where the macro is called, but I don't know how to differentiate between the clj and the cljs ones

Vincent Cantin11:06:46

its :imports contains a lot of Java classes, however

thheller11:06:34

dunno what you mean. this is a clojure namespace yes. just need to check the name

thheller11:06:43

is it the one that has the alias or not?

Vincent Cantin11:06:52

the name is the namespace where the macro is called

Vincent Cantin11:06:59

yes, the one with the alias

thheller11:06:21

I guess I don't know how eval resolves symbols then

thheller13:06:18

the analyzer warning should be fixed in 2.19.3

💜 1
tianshu10:06:01

How should I understand this error, I'm trying to use the worker threads in nodejs target

SHADOW import error /home/tianshu/workspace/race-poker/racepoker-transactor/.shadow-cljs/builds/worker/dev/out/cljs-runtime/shadow.module.main.append.js

thheller10:06:11

I don't know. where is the rest of it? I mean that message is just telling you what failed to load. *why* should be below it?

tianshu11:06:34

Hmm, there's no more information. Probably because it happens in a worker thread. I think it's some error, maybe. I'm struggling with how to async/await in clojurescript. Because I have to let a script keep running, and receive messages.

tianshu11:06:30

I saw in JavaScript, they do

async function() {
    await new Promise(resolve => setTimeout(resolve, 1000);
}

chrisbroome12:06:06

Just to be clear, you’d want to return that promise in JavaScript in order to await on it:

async function delayOneSecond() {
    return await new Promise(resolve => setTimeout(resolve, 1000);
}
or via arrow notation which is more idiomatic now, automatically returns if it’s just 1 statement, and has the benefit that it can’t be called as a JS constructor (i.e. it has no this binding):
const delayOneSecond = async () => await new Promise(resolve => setTimeout(resolve, 1000))
Interestingly enough in JS because of the way promises chain you don’t need to explicitly await a promise if it’s the return value of the async function - they are automatically unwrapped, so the following code is equivalent:
const delayOneSecond = async() => new Promise(resolve => setTimeout(resolve, 1000))

thheller11:06:39

there should be more information? I mean the above is just log output. there will be an actual exception thrown which should be available somewhere?

thheller11:06:04

in node script should remain running if its waiting for network input or something?

thheller11:06:33

I mean the above you can just write without promise or await

thheller11:06:09

(defn wait-forever [] (js/setTimeout wait-forever 1000)) and then calling (wait-forever) once somewhere

thheller11:06:25

you probably want some kind of wait to stop that waiting though 😉

tianshu11:06:32

I don't know why I can't reproduce this problem now. When it happens, there's no more information, only this line.

thheller11:06:07

then whatever you are using to run this is catching and discarding exceptions

thheller11:06:28

which seems like a bad idea. or you are just missing an arguments or so that would handle this

thheller11:06:15

given that its node maybe an (.on worker "error" (fn [e] ...)) or so

tianshu11:06:17

Yeah, you're right. Just have one timeout, will prevent from exit.

tianshu11:06:33

I want to run something represent a game room, and receive / send events. Main thread is used for websocket connection, and the game logic is running inside workers.

tianshu11:06:04

Yeah, it exits because I have some error.

tianshu11:06:37

It's a bit confusing when somewhere has an error, because if you don't catch it, it has no information at all.

thheller11:06:09

do you have an (.on worker "error" (fn [e] ...))?

thheller11:06:14

I mean it should end up there?

thheller11:06:37

never used workers in node so can't really say but that would be my expectation

tianshu11:06:01

Yep, I have one

tianshu11:06:10

Maybe it's my case, I've tried browser-repl build a few days ago, when I execute code with an error, the repl will tell :repl/exception . But nowhere I can see the error, unless I wrap it with a try/catch

thheller11:06:53

is that with emacs? if so maybe try without? the :repl/exception should always be coming together with the actual error. emacs maybe hiding that part somewhere

tianshu11:06:56

Unfortunately, yes

tianshu11:06:12

I still didn't get used to other editor

tianshu11:06:23

Yep, it could be.

thheller11:06:39

fine to use that editor. just maybe try just shadow-cljs browser-repl and evaling whatever to find the actual problem

tianshu11:06:30

But if that error happen in browser. Will it be caught, so I couldn't see it inside browser console?

tianshu11:06:42

I will do that after my work

thheller11:06:45

no. meaning the exception is caught if you eval at the REPL because thats always in a try/catch and it'll return that to the REPL. emacs just seems to maybe hide it. dunno, should be printed to stderr. wherever that ends up in emacs

tianshu11:06:29

Yeah, will try it again