shadow-cljs 2025-05-20

Now I am fighting with nodejs built-in modules, keep running into these errors when loading the compiled nodejs script:

shadow-cljs - failed to load module$node_modules$$google_cloud$firestore$build$src$index

Module not provided: shadow.esm.esm_import$process

node:internal/modules/esm/module_job:387
    const namespace = this.module.evaluateSync();
                                  ^

Module not provided: shadow.esm.esm_import$process
This is with :target :esm :runtime :node

with :js-provider :import or :keep-as-import #{"process"}?

:js-options {:keep-native-imports true
                                   :keep-as-import 
                                   #{"trace_events"
                                     "async_hooks"
                                     "wasi"
                                     "perf_hooks"
                                     "request"
                                     "inspector"
                                     "diagnostics_channel"
                                     "fs"
                                     "os"
                                     "path"
                                     "http"
                                     "http2"
                                     "https"
                                     "zlib"
                                     "stream"
                                     "crypto"
                                     "tty"
                                     "net"
                                     "tls"
                                     "querystring"
                                     "child_process"
                                     "dns"
                                     "worker_threads"
                                     "module"
                                     "constants"


                                     ;; Weird and annoying libraries
                                     "re2"
                                     "canvas"
                                     "jsdom"

                                     "firebase-admin/app"
                                     "firebase-admin/auth"
                                     "firebase-admin/storage"
                                     "firebase-admin/functions"
                                     "@google-cloud/firestore"
                                     "@elastic/elasticsearch"
                                     }

                                   :ignore-asset-requires true}

But yeah with :keep-as-import

I might be blind but "process" is not in that set?

:keep-native-imports is not an option that exists

Oh my bad I added this into shadow.build.resolve

(or
      (str/starts-with? require "esm:")
      (str/starts-with? require "https://")
      (str/starts-with? require "http://")
      (and
        (:keep-native-imports js-options)
        (or (contains? native-node-modules require) (str/starts-with? require "node:")))
      (contains? (:keep-as-import js-options) require)) 

I can remove it and fix the :keep-as-import list

For some reason it broke with all of these libs:

"firebase-admin/app"
                                     "firebase-admin/auth"
                                     "firebase-admin/storage"
                                     "firebase-admin/functions"
                                     "@google-cloud/firestore"
                                     "@elastic/elasticsearch"

That is why I was adding them

I assume you are not using :js-provider :import because of this css stuff?

I got the same error for all of the libs about shadow.esm.esm_import$process

And yes you are correct because of the css stuff

what confuses me is failed to load module$node_modules$$google_cloud$firestore$build$src$index

that is not a file it should be loading if you have excluded it

so it should just import "@google-cloud/firestore" directly from node

Oh yeah sorry I added that later

currently it is:

shadow-cljs - failed to load module$node_modules$agentkeepalive$lib$agent

Module not provided: shadow.esm.esm_import$process
shadow-cljs - failed to load module$node_modules$agentkeepalive$index
Module not provided: shadow.esm.esm_import$process

node:internal/modules/esm/module_job:387
    const namespace = this.module.evaluateSync();
                                  ^

Module not provided: shadow.esm.esm_import$process

So sorry about that

did you restart the build sometime inbetween changing all those exludes?

Yes and als did a clean of the .shadow-cljs folder and the target folder

I do have multiple builds running so I could try to only run the one build

nah builds don't share anything, so should not matter

unless of course they use the same :output-dir?

No they don't

But I am now only running the one build to be sure

ok, then they can't affect each other

Still running into exactly the same error

maybe I need to add an inverse like :only-bundle #{"that-css-thing"} 😛

Yeah I was just thinking the same thing

only bundle the @atlaskit crap

its always a bit sketchy to only bundle partially because some of the included things may end up using the same things and end up with the unbundled stuff

two versions of the same thing is never good

yeah that is never a good idea

And that is a very easy trap to fall into

I don't really get why a shadow.esm.esm_import$process is created I thought it would keep those import calls as is

you could do a quick test and try :js-provider :import but then run the final result through something like ncc

I did the same with rsbuild

And then the project works

would help to have a reproducible example so I can check what exactly is wrong

Sure I'll work on that

I reproduced it:

shadow-cljs - failed to load module$node_modules$agentkeepalive$lib$agent
Module not provided: shadow.esm.esm_import$process
shadow-cljs - failed to load module$node_modules$agentkeepalive$index
Module not provided: shadow.esm.esm_import$process

node:internal/modules/run_main:129
    triggerUncaughtException(
    ^
Module not provided: shadow.esm.esm_import$process
(Use `node --trace-uncaught ...` to show where the exception was thrown)
I'll create a repo

👍 1

Btw I got the same error for other built-in stuff if it tried to require that

fixing this is unfortunately a bit involved. not quite sure how to go about it. currently for esm shadow-cljs will leave esm_import$... references in the code. as part of :advanced optimizations there is an extra closure pass that'll collect these and add the actual required JS import. problem is the sources for the :keep-as-import stuff are :shadow-js, so like any other npm source only runs through :simple and not this path

It currently breaks without advanced optimizations though? Or is it more related to the pass that get's added regardless of the optimizations?

making dev builds work is trivial

Ah ok I did not get that far

problem is making this work without breaking the reasons I did this compiler pass in the first place

I think I have a plan though

🙏 1

try 3.1.3. Module not provided: shadow.esm.esm_import$process should be gone

Damn that was fast

its not the cleanest solution, but should be fine

I will test it out today and report back, if it all works I can remove a lot of crap from our projects

I want to port node class. It works in repl, but release build is crashing. (btw. in my setup in calva no longer works async print from node)

const { TeamsActivityHandler } = require("botbuilder");
class TeamsBot extends TeamsActivityHandler {
  constructor() {
    super();
      this.onMessage(async (context, next) => {
      await context.sendActivity("robotox!");
     await next();
    });))
with defclass
(ns teams-bot
  (:require
   ["botbuilder" :refer [^js TeamsActivityHandler TurnContext]]
   [promesa.core :as p]
   [shadow.cljs.modern :refer [defclass]]))

(defclass TeamsBot
  (extends TeamsActivityHandler)
  (constructor [this]
               (super)
               (.onMessage this (fn [^ĵs context next]
                                  (on-message context)
                                  (p/then (next) identity)))))
It works in repl (btw in calva no longer works async print). but in release build with debug there is crash. I tried ^js, js/*, and :simple
/app/bot.js:11843
    this.$onMessage$(function($context$jscomp$15$$, $next$jscomp$6$$) {
         ^

TypeError: this.$onMessage$ is not a function
    at new $teams_bot_cljs$classdecl$var6$$ (/app/bot.js:11843:10)
    at Function. (/app/bot.js:11995:3)
    at $cljs$core$apply$cljs$0core$0IFn$0_invoke$0arity$02$$ (/app/bot.js:3586:179)
    at /app/bot.js:11991:2
    at Object. (/app/bot.js:11999:3)
    at Module._compile (node:internal/modules/cjs/loader:1364:14)
I aslo don't know how to do await next(); this is my best guess (p/then (next) identity)

you should be getting an extern inference warning?

onMessage is getting renamed

the ^js needs to be on the this in this case

so (.onMessage ^js this ...)

(defclass TeamsBot
  (extends TeamsActivityHandler)
  (constructor [this]
    (super)
    (.onMessage ^js this
      (fn [^js context next]
        (js-await [_ (.sendActivity context "robotox!")]
          (js-await [_ (next)]
            nil))))))

dunno exactly that the next is doing there or why its waiting for it, probably can just by (next) without await

or (-> (.sendActivity context ...) (.then next))

thanks I tried (constructor [^ĵs this] and it got renamed, thanks,

hmm that should be fine too

this await chaining is very strange to me. i am guessing that this is so confusing to me, because clojure return last eval as function result, but js hacks something with await

So I am getting "router deprecated handlers that are Promise-like are deprecated, use a native Promise instead at node_modules/router/lib/route.js:157:13"

that might be complaining about promesa. that has its own weird promise type

yes it is something in p/let because without p/let and just returning (p/promise nil) there are no warns

so probably need to remove promesa

FWIW async functions in JS just implicitly always return a promise

I read somewhere that yours js-await is just for repl?

so without an actual return that just returns a promise that resolves to undefined

doesn't make much sense to use the in the REPL. there was talk about a specific "primitive" in the REPL to make dealing with async code a bit easier

but that doesn't exist yet

i don't see async output in calva, no logss, and prints, so it is quite painfull to do repl dev.

We do

(p/handle promise-thing (fn [res err] (def _res [res err]))
This kinda works for repl dev

Great, But all log statements are not printed when they are invoked in express handlers.

Yeah I just have a console open for that

I am investigating if this is just my problem, because i am running shadow-cljs watch ;bot, and I am connected from calva to nrepl, and there is no output. And I tried different output destinations. but just for node.

It is interesting that (TeamsBot.) is not marked as JS,. and .run is renamed.

(defn run-teams-bot [^js context]
  (let [^js b (TeamsBot.)]
    (.run b context)))

hmm that is very clearly marked?

which shadow-cljs version do you use? could be that the js tag is removed because the type is inferred by other code? could just be a bug

or do you mean that it gets renamed if the ^js tag is not there. that would be expected than and should produce an inference warning

3.1.1 in case (.run (TeamsBot.)) .run was renamed, even (.run ^ĵs (TeamsBot.)) got renamed wo warnings

but now i have bigger problem:

/app/node_modules/botbuilder-core/src/turnContext.ts:557
                    this.responded = true;
                                  ^


TypeError: Cannot perform 'set' on a proxy that has been revoked
    at Proxy.<anonymous> (/app/node_modules/botbuilder-core/src/turnContext.ts:557:35)
    at Generator.next (<anonymous>)
    at fulfilled (/app/node_modules/botbuilder-core/src/turnContext.ts:4:4)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

I tried some other unusual ^js annotations .run is recognised in this case

(defn async-run-teams-bot
  [^js context]
  (let [b (TeamsBot.)]
    (p/let [_ (.run ^js b context)])))
this case is also ok:
(let [b (TeamsBot.)]
    (js-await [_ (.run ^js b context)]))
but not
(js-await [_ (.run (TeamsBot.) context)])

(js-await [_ (.run ^js (TeamsBot.) context)])

(let [b (TeamsBot.)]
  (js-await [_ (.run ^js (TeamsBot.) context)])

(let [b (TeamsBot.)]
  (p/let [_ (.run ^js  b context)])))
 (p/let [_ (.run (new TeamsBot) context)]))
 (p/let [_ (.run (TeamsBot.) context)])
 (p/let [_ (.run ĵs (TeamsBot.) context)])
 (p/let [_ (.run (new TeamsBot) context)])
And this is also strange (:require ["botbuilder" :refer [TurnContext]]) ok (.getConversationReference ^js *TurnContext* (.-activity context)) not ok: (*TurnContext*.getConversationReference (.-activity context))

(TurnContext.getConversationReference (.-activity context)) this not working is sort of expected since this is syntax that only accidentally works

(.getConversationReference TurnContext (.-activity context)) this should be fine and not require ^js hints?