Fork me on GitHub
#shadow-cljs
<
2022-01-22
>
davidst11:01:12

Is there a way to get a REPL for a build with target :esm and runtime :node? The build configuration:

{:target           :esm
   :js-options       {:js-provider :import}
   :runtime          :node
   :output-dir       "out/esm"
   :modules
   {:script {:entries [my.script.core]
             :exports {init my.script.core/init}}}}
The output produced by npx shadow-cljs watch ... for this build seems to not connect to the server and node out/esm/script.js exists immediately. I have tried to make it connect to the server by requiring the namespace shadow.cljs.devtools.client.node in my.script.core . But then running the output with node ... reports the following error:
var socket = (new shadow.esm.esm_import$ws(ws_url,({"rejectUnauthorized": false})));
              ^

TypeError: shadow.esm.esm_import$ws is not a constructor
    at shadow$cljs$devtools$client$node$start (file:///.../out/esm/cljs-runtime/shadow.cljs.devtools.client.node.js:122:15)
    at Object.attempt_connect_BANG_ (file:///.../out/esm/cljs-runtime/shadow.cljs.devtools.client.shared.js:482:128)
    at Object.shadow$cljs$devtools$client$shared$init_runtime_BANG_ [as init_runtime_BANG_] (file:///.../out/esm/cljs-runtime/shadow.cljs.devtools.client.shared.js:970:16)
    at file:///.../out/esm/cljs-runtime/shadow.cljs.devtools.client.node.js:431:36

davidst11:01:16

I also tried to use node-script as a target but that doesn't seem to be an option with some npm packages that are ESM only (e.g., the latest version of chalk).

thheller15:01:07

@davidst I didn't try to get the node repl running with :esm yet. you can try setting :runtime :custom :devtools {:client-ns some.ns} where some.ns is just a copy of https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/cljs/devtools/client/node.cljs with the ["ws" :as ws] changed to ["ws$default" :as ws]. maybe thats enough?

thheller15:01:50

I suspect there might be more changes necessary though

davidst23:01:57

Thank you. Following your suggestion I was able to connect to the repl. Unfortunately, when evaluating anything, I was getting errors about undefined SHADOW_IMPORTED and SHADOW_IMPORT. It looks like these are defined in node_bootstrap.txt for node targets. It seems shadow/boot/esm.js is supposed to replace (some of) that. I have tried to fix these errors. It seems to work alright so far but it is clearly a nasty hack:

;; these implementations replace the first three functions in shadow.cljs.devtools.client.node

(defn node-eval [{:keys [js source-map-json] :as msg}]
  (let [result (js* "(0,eval)(~{});" js)]
    result))

(defn is-loaded? [src]
  (true? (js/SHADOW_ENV.setLoaded src)))

(defn closure-import [src]
  {:pre [(string? src)]}
  (js/console.log src)
  (when-not (= src "goog.base.js")
    ;; goog base doesn't work with this kind of import?
    (js/globalThis.shadow_esm_import (str "./cljs-runtime/" src)))
  (js/SHADOW_ENV.setLoaded src))

;; this interface implementation replaces the current one for the type cljs-shared/Runtime
api/IEvalJS
(-js-eval [this code]
  (js* "(0,eval)(~{});" code))

thheller06:01:27

this probably needs many more adjustments. the regular node impl assumes it can do sync imports but the shadow_esm_import is async so all of the code needs to be adjusted to not assume sync

thheller06:01:06

might need to go with loading the file from disk using fs/readFileSync and then eval'ing

thheller06:01:14

haven't spend much time on node with esm so not sure what kind of restrictions exist. might even require modifying the source get get rid of import statements. I opened https://github.com/thheller/shadow-cljs/issues/979

thheller06:01:25

not sure when I can get to it though. pretty busy currently.

davidst23:01:29

The hack to prepend "./cljs-runtime" to src seems especially fragile. Is there a better way to do that?

w-n-c23:01:02

Complete newbie using the default shadow-cljs setup in luminus. App loads fine but test throws a fit. Is this a 'need to be using :broswer-test instead of :node-test' type issue? I'm struggling to figure out where to even look to troubleshoot what wrong beyond an import expects the browser 'window' object to exist. Its one test file for a re-frame subscription if that helps at all

ReferenceError: window is not defined
    at [path]/.shadow-cljs/builds/test/dev/out/cljs-runtime/accountant/core.cljs:21:35
    at [path]/.shadow-cljs/builds/test/dev/out/cljs-runtime/accountant.core.js:16:3
    at global.SHADOW_IMPORT ([path]/target/test/test.js:64:44)
    at [path]/target/test/test.js:1701:1
    at Object.<anonymous> ([path]/target/test/test.js:1781:3)
    at Module._compile (node:internal/modules/cjs/loader:1097:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1149:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
===============================================

Apple01:01:26

perhaps more of an interop issue. window or js/window?

w-n-c02:01:50

I'm not calling window anywhere but I suppose one of the libraries I'm using must be. I swapped to :browser and am at least past ^ error message. I was hoping to avoid having to set up Karma and a browser-driven test suite for now by just testing my re-frame logic with :node-test but its been more of a hassle to troubleshoot than it is to just launch Karma. Anyways, thanks for taking a look!

thheller06:01:02

this library is using js/window. so it just isn't compatible with anything that doesn't have that. from the looks of it there are many other things that expect a full browser

thheller06:01:50

if you can move the require for that lib to some other place yuo might be able to run it in node. can't say without seeing code.

👍 1
w-n-c07:01:05

ahhh thanks thheller, I mistakenly assumed 'accountant' was some shadow-cljs interal, but its an internal dependency of my routing lib. Sorry haha I'm still struggling with the normal clj stacktraces so I kinda just assumed this was another opaque one 😅. I'll deps :tree next time