Fork me on GitHub
#shadow-cljs
<
2022-04-07
>
avi00:04:57

👋 Does shadow require that “clojure tools” (i.e. the clojure script) be installed?

avi00:04:07

Asking because I don’t see it listed in the docs, but…

thheller00:04:24

only if you intend on using them. by default no.

avi00:04:29

When I uninstall clojure tools and then try to compile my project with shadow, I get an error

avi00:04:37

Is it maybe because my project is using deps.edn ?

avi00:04:11

The error:

throw er; // Unhandled 'error' event
      ^

Error: spawn clojure ENOENT
    at Process.ChildProcess._handle.onexit (node:internal/child_process:283:19)
    at onErrorNT (node:internal/child_process:476:16)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
Emitted 'error' event on ChildProcess instance at:
    at Process.ChildProcess._handle.onexit (node:internal/child_process:289:12)
    at onErrorNT (node:internal/child_process:476:16)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'spawn clojure',
  path: 'clojure',
  spawnargs: [ '-M', '-m', 'shadow.cljs.devtools.cli', '--npm', 'server' ]
}

Node.js v17.8.0

thheller00:04:36

if you set :deps in shadow-cljs.edn that tells shadow-cljs to use the clojure tool and deps.edn yes

avi00:04:43

I see, ok, thanks!

avi00:04:59

Maybe that should be added to the docs under the installation section which lists prerequisites?

thheller00:04:30

it is listed in the docs. you chose to use it. nothing in shadow-cljs requires you to use it.

avi00:04:32

Ah I see that in there now

avi00:04:37

Yeah that’s good

avi00:04:48

It’s just… I didn’t realize this.

avi00:04:55

People don’t always read the docs in a linear fashion

avi00:04:39

In my case I thought “I wonder if I can omit the installation of Clojure Tools in my CI/CD environment” — so I checked the prerequisites at the top of the docs in the installation section and it wasn’t listed there, so I figured it was safe to omit it.

avi00:04:50

I’d be happy to submit a PR if you’d like

thheller00:04:49

again .. not a prerequisite unless you specifically configure it to use those tools

thheller00:04:58

so listing it there would be wrong

avi00:04:30

well, ok, I would have listed it with a note that it’s only required if you’re using deps.edn, but ok, no worries, thank you!

Fahd El Mazouni08:04:56

Hi ! is there a way to explicitly ignore specific warnings for specific namespaces without using warnings-as-errors?

pinkfrog14:04:38

Suddenly I am seeing errors from shadow:

[2022-04-07 22:51:42.126 - WARNING] :shadow.cljs.devtools.server.worker.impl/cljs-compile-ex - {:input {:code "(clojure.core/refer-clojure)", :ns app.wallet, :repl true}}
ExceptionInfo Failed to process REPL command {:eof? false, :ns app.wallet, :form (clojure.core/refer-clojure), :source "(clojure.core/refer-clojure)", :tag :shadow.cljs.repl/process-ex}
        shadow.cljs.repl/process-read-result (repl.clj:502)
        shadow.cljs.repl/process-read-result (repl.clj:476)
        shadow.cljs.repl/process-input (repl.clj:661)
        shadow.cljs.repl/process-input (repl.clj:639)
        shadow.cljs.devtools.server.worker.impl/fn--14599 (impl.clj:698)
        shadow.cljs.devtools.server.worker.impl/fn--14599 (impl.clj:688)
        clojure.lang.MultiFn.invoke (MultiFn.java:234)
        shadow.cljs.devtools.server.util/server-thread/fn--14273/fn--14274/fn--14282 (util.clj:269)
        shadow.cljs.devtools.server.util/server-thread/fn--14273/fn--14274 (util.clj:268)
        shadow.cljs.devtools.server.util/server-thread/fn--14273 (util.clj:241)
        java.lang.Thread.run (Thread.java:829)
Caused by:
ExceptionInfo ns* not supported (require, require-macros, import, import-macros, ... must be part of your ns form) {:rename-macros nil, :renames {}, :use-macros nil, :excludes #{}, :name app.wallet, :op :ns*, :env {:fn-scope [], :locals {}, :js-globals {console {:op :js-var, :name console, :ns js}, location {:op :js-var, :name location, :ns js}, escape {:op :js-var, :name escape, :ns js}, screen {:op :js-var, :name screen, :ns js}, global {:op :js-var, :name global, :ns js}, process {:op :js-var, :name process, :ns js}, require {:op :js-var, :name require, :ns js}, alert {:op :js-var, :name alert, :ns js}, history {:op :js-var, :name history, :ns js}, window {:op :js-var, :name window, :ns js}, module {:op :js-var, :name module, :ns js}, exports {:op :js-var, :name exports, :ns js}, document {:op :js-var, :name document, :ns js}, navigator {:op :js-var, :name navigator, :ns js}, unescape {:op :js-var, :name unescape, :ns js}}, :ns {:rename-macros nil, :renames {}, :meta {:file "app/wallet.cljs", :line 1, :column 5, :end-line 1, :end-column 15}, :ns-aliases {cljs.loader shadow.loader, react shadow.js.shim.module$react, clojure.pprint cljs.pprint, clojure.spec.alpha cljs.spec.alpha}, :use-macros nil, :excludes #{}, :shadow/js-access-properties #{"Text"}, :name app.wallet, :reader-aliases {}, :js-aliases {"react-native" shadow.js.shim.module$react_native}, :imports nil, :requires {cljs.core cljs.core, goog goog, shadow.js.shim.module$react_native shadow.js.shim.module$react_native, rn shadow.js.shim.module$react_native}, :seen #{:require}, :uses nil, :defs {wallet-tab {:protocol-inline nil, :meta {:file "app/wallet.cljs", :line 5, :column 7, :end-line 5, :end-column 17, :arglists (quote ([]))}, :name app.wallet/wallet-tab, :file "app/wallet.cljs", :end-column 17, :method-params ([]), :protocol-impl nil, :arglists-meta (nil nil), :column 1, :variadic? false, :line 5, :ret-tag cljs.core/IVector, :end-line 5, :max-fixed-arity 0, :fn-var true, :arglists (quote ([]))}}, :require-macros {cljs.core cljs.core}, :cljs.analyzer/constants {:seen #{:>}, :order [:>]}, :flags {:require #{}}, :js-deps {"react-native" {:as rn}}, :deps [goog cljs.core shadow.js.shim.module$react_native]}, :def-emits-var true, :shadow.build/mode :dev, :column 1, :shadow.build/tweaks false, :line 1, :shadow.build.compiler/repl-context true, :context :expr}, :imports nil, :requires nil, :uses nil, :reload {:use nil, :require nil, :use-macros nil, :require-macros nil}, :require-macros nil, :form (ns* (:refer-clojure)), :reloads {}, :deps []}
        shadow.build.compiler/post-analyze (compiler.clj:94)
        shadow.build.compiler/post-analyze (compiler.clj:89)
        shadow.build.compiler/analyze/fn--12392 (compiler.clj:265)
        shadow.build.compiler/analyze (compiler.clj:252)
        shadow.build.compiler/analyze (compiler.clj:211)
        shadow.cljs.repl/repl-compile/fn--14080/fn--14081 (repl.clj:441)
        shadow.cljs.repl/repl-compile/fn--14080 (repl.clj:414)
        shadow.cljs.repl/repl-compile (repl.clj:412)
        shadow.cljs.repl/repl-compile (repl.clj:409)
        shadow.cljs.repl/process-read-result (repl.clj:500)
        shadow.cljs.repl/process-read-result (repl.clj:476)

pinkfrog14:04:23

The error is caused by eval a whole namespace in the cljs repl.

pinkfrog14:04:43

I’d like to know what exactly the error is complaining about?

pinkfrog14:04:53

The relevant code is:

(ns app.wallet
  (:require
   ["react-native" :as rn]))

(defn wallet-tab
  []
  [:> rn/Text
   "Wallet"])

(comment
  (js/console.log "dasdf"))

pinkfrog14:04:06

I switched to another namespace, even eval a simple a namespace such as

(ns app.event)

(def initialize-app ::initialize-app)
(def set-active-view ::set-active-view)
will also result in the shadow error.

pinkfrog15:04:57

I asked in the #calva channel and turns out it is a regression of calva.

pez15:04:10

Evaluating (refer-clojure) throws an error in shadow-cljs and also prints a huge map

{:rename-macros nil, :renames {}, :use-macros nil, :excludes #{}, :name calva.fmt.formatter, :op :ns*, :env {:fn-scope [], :locals {}, :js-globals {console {...
Is this a bug? It's fine evaluating that in a vanilla ClojureScript REPL.

pez16:04:03

Here's a full output log.

thheller17:04:26

yeah, its just not implemented yet

thheller17:04:51

to be perfectly honest I don't even know what refer-clojure is supposed to do 😛

pez17:04:01

I was unaware of it until recently. Turns out that a lot of errors that we instruct Calva users to fix by loading the file is because we just do (in-ns …) and that switches to the namespace, but doesn't make Clojure core available. Then some while ago I read the in-ns info on this page a bit more carefully: https://clojure.org/guides/repl/navigating_namespaces#_switching_to_an_existing_namespace_with_in_ns

thheller17:04:03

well thats specific to CLJ. CLJS works a bit different there

thheller17:04:58

in CLJ refere-clojure is just a function. in CLJS it technically doesn't even exist since it can't do what it does in CLJ

thheller17:04:46

so it is implemented as sort of a hack via a macro

pez18:04:23

I actually don't know if the problem it solves for clj is there for cljs. I can test that. Hopefully I can skip calling it for cljs. But if not, it would be good to not have to check if it’s shadow-cljs or vanilla cljs.

pez19:04:51

OK. So just confirmed what you probably knew, @U05224H0W: (refer-clojure) isn't needed in ClojureScript REPLs. So then I'll mainly skip that for cljs sessions and am good with that.

thheller19:04:42

well technically it is still invalid to call in-ns before the ns exists. even in cljs

pez20:04:12

It doesn't seem to cause problems in practice, though? And that guide seems to be cool with it as long as we call refer-clojure.

pez20:04:28

That said. Maybe it is better to do something like (if (find-ns 'foo) (in-ns 'foo) (ns foo))?

pesterhazy17:04:28

Question about reloading and component-local state. I've set up Shadow to reload my code using https://github.com/pesterhazy/shadow-jest/blob/tictactoe/src/main/tdd/main.cljs#L5, which works fine. However, I'm seeing that component-local state gets lost. My code uses React's useState (here's a https://github.com/pesterhazy/shadow-jest/blob/tictactoe/src/main/tdd/domain/board.cljs#L9 to the code). From reagent I'm used to defonce'd state atoms, which would preserve inputs. This is very useful because while debugging/applying styles I don't need to manually recreate state every time I change a file. Now after moving to modern React and useState, is there a way to both • rerender the components after reload to let the new code take effect and • preserve the component-local state?

thheller17:04:02

this is not something shadow-cljs is involved in really. it is either react or reagent. they manage the state, shadow-cljs just calls a function

thheller17:04:15

generally I would say that local state is always lost

thheller17:04:22

I don't know how react-refresh handles preserving useState but it seemed quite involved and required support from the compiler

2
pesterhazy17:04:59

I didn't know about react-refresh, that's an interesting pointer, thanks!

pesterhazy18:04:21

Oh, as you say React Fast Refresh seems incredibly complicated, at least compared to what CLJS has had, basically, forever

pesterhazy18:04:01

I knew that my question wasn't strictly about shadow-cljs, but given that it does reloading so well this channel felt like a good place to ask

thheller18:04:39

well yeah if you wanted something like react-refresh then shadow-cljs would need to get more involved. I can't really figure out what it would need to do though. likely some compiler modifications that would get icky 😛

Jimmy Miller04:04:31

Helix already has support for react refresh. I've used it a lot and never had any troubles. https://github.com/lilactown/helix/blob/master/docs/experiments.md#fast-refresh

👀 1
pesterhazy20:04:29

I used another approach for now. I created a "playground" part of the app, which has data pre-filled in. So I can use that to edit styles

pesterhazy20:04:27

This kind of stuff mostly just worked with reagent/re-frame's global state atom. I didn't realize this was such a big advantage of the (otherwise icky) global var

dpsutton20:04:55

Not sure if you’ve looked into devcards. My favorite dev experience was using bruce’s test runner in one tab, the devcards in another tab, and the “real” tab in a third. Multiple entries on a build to enable all of this was a really great experience

pesterhazy20:04:27

Yeah, really love devcards as well. For now, I'm keeping it simple with a https://github.com/pesterhazy/shadow-jest/blob/tictactoe/src/main/tdd/main.cljs#L12

👍 1
pesterhazy20:04:23

While writing this I discovered that shadow-cljs's dev webserver uses a trick to serve index.html back to arbitrary urls but only when used as the entry point - not for images, css etc. I was confused as to how this could possibly work. Turns out it checks if "text/html" is https://github.com/thheller/shadow-cljs/blob/ba0a02aec050c6bc8db1932916009400f99d3cce/src/main/shadow/http/push_state.clj#L12. Ingenious approach that I haven't seen elsewhere

pesterhazy20:04:19

The benefit is that you still get nice 404 if you mistype a jpg url in your code (whereas other popular ad-hoc webservers just blindly serve you index.html for any route that's not found)

❤️ 1