Fork me on GitHub
#fulcro
<
2021-07-09
>
sheluchin13:07:40

I'm trying to integrate Crux into my app, following the example from https://github.com/fulcrologic/fulcro-rad-demo/pull/24/files. Although I'm not using RAD, I figure the setup is roughly the same anyhow. I notice that the example is using mount for a bunch of stuff, like here https://github.com/fulcrologic/fulcro-rad-demo/pull/24/files. I don't have any experience with mount (or Component). I kinda get what it does from the description in the repo, but it's still new to me. My question is should I dive in and figure out mount and how to use it, or would it be reasonable to try to integrate Crux without the use of mount?

tony.kay14:07:09

depends on what you're building. If you want a reliable, restartable REPL experience for Clojure you're going to need something like mount or component in order to properly shut down resources, reload code, and start them back up. There are definitely trade-offs for both mount and component. mount is a lot less code, and figures out the dep tree from your dependency tree; however, you can easily make mistakes about what is and is not a stateful thing. Basic rule I use: If something else in the program HAS to use the thing you're making into a defstate, then that's fine. If you are relying purely on the side-effect of defstate start/stop, then that is NOT what you want...instead make that a function and call it from something that is used. Logging is the one I made this mistake on: the configuration of it is stateful, but nothing uses a defstate that just side-effects to configure logging, and if nothing requires it, then it won't get called. Technically it will still work if you remember to require it, but that has the danger that someone will later "clean up" that unused require.

tony.kay14:07:43

So, if you're making a toy, you don't need one of these...but if you're doing anything else I'd say you should definitely set it up.

lgessler14:07:07

you definitely want to use something like mount (i.e. an application state management lib) with crux. IMO mount is a good choice since it's quite conceptually small and easy to get going with. if you want to see a minimal setup with crux and mount see https://github.com/lgessler/glam/blob/master/src/main/glam/server/crux.clj#L47L53 in a project I have

tvaughan15:07:50

$ cat src/example/server/core.clj
(ns example.server.core
  (:refer-clojure :exclude [run!])
  (:gen-class)
  (:require
   [example.server.db.mounts]
   [example.server.http.mounts]
   [example.server.settings.mounts]
   [mount.core :as mount]))

(defn add-shutdown-hook
  [fn]
  (.addShutdownHook (java.lang.Runtime/getRuntime) (Thread. ^Runnable fn)))

(defn shutdown!
  []
  (mount/stop))

(defn run!
  []
  (mount/start
   #'example.server.settings.mounts/settings
   #'example.server.db.mounts/conn
   #'example.server.http.mounts/http)
  (add-shutdown-hook shutdown!)
  (println "Go!"))

(defn -main
  [& _]
  (run!))

$ cat src/example/server/http/mounts.clj
(ns example.server.http.mounts
  (:require
   [example.server.http.core :as http]
   [example.server.settings.mounts :refer [settings]]
   [mount.core :refer [defstate]]))

;; 
(declare http)

(defstate http :start (http/start settings) :stop (http))

$ cat user/devtools/core.clj
(ns devtools.core
  (:refer-clojure :exclude [run!])
  (:require
   [clojure.tools.namespace.repl :refer [set-refresh-dirs refresh]]
   [example.server.core :as server]))

(set-refresh-dirs "src" "user" "resources/db")

(defn run!
  []
  (server/run!))

(defn shutdown!
  []
  (server/shutdown!))

(comment
  (run!)
  (do
    (shutdown!)
    (refresh :after 'devtools.core/run!))
  42)

$ clojure -M:alias1:alias2 -m example.server.core

tvaughan15:07:48

I also recommend mount

sheluchin10:07:52

@U0P7ZBZCK is that in a public repo somewhere?

tvaughan12:07:31

Sorry @UPWHQK562 but no. We have plans to make our template publicly available, but other priorities have distracted us from doing so

sheluchin12:07:24

No worries, thanks for the snippet.

tvaughan13:07:52

Happy to answer any questions

sheluchin17:07:09

@U0P7ZBZCK I notice that none of the sample projects I've looked at put the query logic in the resolvers file. This is a bit surprising to me, because looking at the example here https://book.fulcrologic.com/#_parsing_queries, it seems like resolvers.clj would be the perfect place for read query logic. Is this simply a matter of arbitrary organization? If I'm following the examples in the Fulcro book, would it be reasonable to put my Crux queries in resolvers.clj and mutations.clj?

tvaughan17:07:40

We use Datomic but I think the answer is the same. Yes, this is what we do:

(ns example.server.http.parsers
  (:require
   [com.wsscode.pathom.connect :as pathom-connect]
   [com.wsscode.pathom.core :as pathom]
   [example.server.data.resolvers.core :as resolvers]
   [example.shared.data.mutations.core :as mutations]))

(defn- ex-handler
  [_ ex]
  (.printStackTrace ex)
  (pathom/error-str ex))

(defonce ^:private documents-handlers [resolvers/documents-resolvers mutations/documents-mutations])

(defonce ^:private documents-parser
  (pathom/parser
   {::pathom/env {::pathom/reader [pathom/map-reader
                                   pathom-connect/reader2
                                   pathom-connect/open-ident-reader
                                   pathom-connect/index-reader]
                  ::pathom/process-error ex-handler
                  ::pathom-connect/mutation-join-globals [:tempids]}
    ::pathom/mutate pathom-connect/mutate
    ::pathom/plugins [(pathom-connect/connect-plugin {::pathom-connect/register documents-handlers})
                      pathom/elide-special-outputs-plugin
                      pathom/error-handler-plugin
                      pathom/trace-plugin]}))

tvaughan17:07:37

(ns example.server.data.resolvers.documents
  (:require
   [com.wsscode.pathom.connect :as pathom-connect]
   [com.wsscode.pathom.core :as pathom]
   [example.server.db.api :as db]
   [example.server.db.mounts :refer [conn]]
   [example.server.utils.permissions :as permissions]))

(pathom-connect/defresolver document-resolver
  [{:keys [::pathom/parent-query request]} {:keys [document/id]}]
  {::pathom-connect/input #{:document/id}
   ::pathom-connect/output [:document/id :document/description :document/created-at]}
  (if (permissions/can-read-document? (:session request) conn id)
    (let [permanent-document-id id]
      (db/q-by-ident (db/conn->db conn) [:document/id permanent-document-id] parent-query))
    {:document/id id}))

(defonce resolvers [document-resolver])

sheluchin18:07:14

Thanks again. That's in line with what I was thinking.

tvaughan18:07:44

(ns example.shared.data.mutations.documents
  (:require
   [com.wsscode.pathom.connect :as pathom-connect]
   [example.server.db.api :as db]
   [example.server.db.mounts :refer [conn]]
   [example.server.utils.documents :as documents]
   [example.server.utils.permissions :as permissions]
   [toolbox.datetime :as datetime]
   [toolbox.random :as random]))

(pathom-connect/defmutation create-document!
  [{:keys [request]} {:keys [drawing/kind]}]
  {::pathom-connect/output [:document/id]}
  (let [permanent-document-id (random/random-id)
        permanent-document-created-at (datetime/now)
        editor-session-id (random/random-id)]
    (documents/copy-template-kind! kind permanent-document-id editor-session-id)
    (let [token-id (-> request :session :token/id)
          user-id token-id]
      (db/tx! conn [{:db/id "editor-session-ref"
                     :editor-session/id editor-session-id}
                    {:document/id permanent-document-id
                     :document/owner-ref [:user/id user-id]
                     :document/status :document.status/draft
                     :document/description (str "New document created at " permanent-document-created-at)
                     :document/editor-session-ref "editor-session-ref"
                     :document/created-at permanent-document-created-at}])
      (documents/spawn-editor! conn permanent-document-id)
      {:document/id permanent-document-id})))

(defonce mutations [create-document!])

sheluchin18:07:58

Are you able to show me a tree of that repo so I can see your organization?

tvaughan18:07:16

.
├── server
│   ├── core.clj
│   ├── data
│   │   └── resolvers
│   │       ├── accounts.clj
│   │       ├── core.clj
│   │       ├── documents.clj
│   │       └── sessions.clj
│   ├── db
│   │   ├── api.clj
│   │   ├── cmdline.clj
│   │   ├── core.clj
│   │   ├── migrations.clj
│   │   └── mounts.clj
│   ├── http
│   │   ├── core.clj
│   │   ├── mounts.clj
│   │   ├── parsers.clj
│   │   └── sessions.clj
│   ├── settings
│   │   ├── core.clj
│   │   └── mounts.clj
│   └── utils
│       ├── documents.clj
│       ├── passwords.clj
│       └── permissions.clj
├── shared
│   └── data
│       └── mutations
│           ├── core.cljc
│           ├── documents.clj
│           ├── documents.cljs
│           ├── errors.clj
│           ├── errors.cljs
│           ├── sessions.clj
│           ├── sessions.cljs
│           ├── toasts.clj
│           ├── toasts.cljs
│           ├── ui.clj
│           └── ui.cljs
├── ui
│   ├── actions.cljc
│   ├── client.cljs
│   ├── components.cljc
│   ├── core.cljc
│   ├── dashboard.cljc
│   ├── documents.cljc
│   ├── hist.clj
│   ├── hist.cljs
│   ├── icons.cljc
│   ├── interop.cljc
│   ├── messages.clj
│   ├── messages.cljs
│   ├── preload.cljs
│   ├── routes.cljc
│   ├── sessions.cljc
│   ├── settings.cljs
│   ├── speedbumps.cljc
│   ├── tailwindui.cljs
│   ├── toasts.cljc
│   ├── updated_at.cljc
│   └── urls.cljc
└── x

11 directories, 52 files

sheluchin18:07:19

The project directory structure constantly has me second guessing myself, I guess because there is no "right" way and everyone applies their own preferences.

sheluchin18:07:39

Very nice 🙇

tvaughan18:07:46

Though at some point we'll switch to #pathom

sheluchin18:07:28

What do you mean? Looks like you're using Pathom already.

tvaughan18:07:15

Oops. Sorry, I meant #polylith

sheluchin18:07:07

I'm not familiar with that one. Will check it out, although the Pathom talks by Wilker were a big part of the reason I wanted to get into Fulcro in the first place.

tvaughan18:07:29

Yeah pathom is very slick. Although EQL and datalog queries are pretty compatible. Beyond permission checking you can pretty much pass the queries through to crux/datomic as-is which could handle everything. I suspect pathom would be much more helpful with a relational database

sheluchin19:07:53

You don't get much out of the function call abstraction Pathom gives you when using EQL/datalog queries?

tvaughan19:07:58

Pathom is definitely a help with organizing an api endpoint, but not so much for query resolution - in my experience

lgessler18:07:31

@UPWHQK562 I personally found it helpful to separate crux queries from resolvers because it helps me feel more confident that my crux data model is correct. If you separate them this way it makes it very easy to write tests for them, play around with them in a REPL, and most importantly, to read them and know that it is only these functions that will be making changes to your crux db

lgessler18:07:04

OTOH it's sometimes hard to decide whether a bit of logic belongs in the crux layer or in the resolver layer, and I could imagine projects, especially ones with less complicated data models, where I'd probably want to just roll everything into resolvers

sheluchin18:07:26

@U49U72C4V ah, yeah, I can see how separating the query logic from the resolvers would make the code easier to test with. Thanks for the tip.

lgessler18:07:33

np! I'm curious to hear how it goes for you, so be in touch with any further thoughts 🙂 one more thing--keep in mind that defresolver https://github.com/wilkerlucio/pathom/blob/master/src/com/wsscode/pathom/connect.cljc#L1554L1563, and that for testing you can pull out the function it uses at ::pc/resolve . So that can make testing almost as easy in the approach where you don't separate DB code from resolver code

sheluchin12:07:59

@U0P7ZBZCK I'm trying to use a similar setup to yours, but when I require my user namespace, the server REPL throws up this error I don't understand:

+08:28 $ iced repl :main --force-shadow-cljs --with-cljs -A:dev
OK: CLJS option is enabled.
OK: shadow-cljs project is detected
OK: For shadow-cljs project, start watching instead of starting REPL.
shadow-cljs - config: /project/shadow-cljs.edn  cli version: 2.8.64  node: v12.18.0
shadow-cljs - starting via "clojure"
shadow-cljs - HTTP server available at 
shadow-cljs - server version: 2.11.23 running at 
shadow-cljs - nREPL server started on port 9000
shadow-cljs - watching build :main
[:main] Configuring build.
[:main] Compiling ...
GUARDRAILS IS ENABLED. RUNTIME PERFORMANCE WILL BE AFFECTED.
Mode: :runtime  Async? false  Throw? false
Guardrails was enabled because the CLJS Compiler config enabled it
[:main] Build completed. (304 files, 0 compiled, 0 warnings, 12.05s)
[:main] Compiling ...
[:main] Build completed. (304 files, 0 compiled, 0 warnings, 0.18s)
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See  for further details.
[2021-07-20 08:30:15.786 - WARNING] :shadow.cljs.devtools.server.nrepl-impl/init-ns-ex - {:init-ns sheluchin.user}
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling at (clojure/tools/namespace/file.clj:39:26). #:clojure.error{:phase :compile-syntax-check, :line 39, :co
lumn 26, :source "clojure/tools/namespace/file.clj"}
        clojure.lang.Compiler.analyze (Compiler.java:6808)
        clojure.lang.Compiler.analyze (Compiler.java:6745)
        clojure.lang.Compiler$InvokeExpr.parse (Compiler.java:3820)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7109)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.access$300 (Compiler.java:38)
        clojure.lang.Compiler$LetExpr$Parser.parse (Compiler.java:6384)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7107)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7095)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.analyze (Compiler.java:6745)
Caused by:
RuntimeException No such var: parse/deps-from-ns-decl
        clojure.lang.Util.runtimeException (Util.java:221)
        clojure.lang.Compiler.resolveIn (Compiler.java:7388)
        clojure.lang.Compiler.resolve (Compiler.java:7358)
        clojure.lang.Compiler.analyzeSymbol (Compiler.java:7319)
        clojure.lang.Compiler.analyze (Compiler.java:6768)
        clojure.lang.Compiler.analyze (Compiler.java:6745)
[2021-07-20 08:30:15.864 - WARNING] :shadow.cljs.devtools.server.nrepl-impl/init-ns-ex - {:init-ns sheluchin.user}
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling at (sheluchin/user.clj:1:1). #:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :source "
sheluchin/user.clj"}
        clojure.lang.Compiler.load (Compiler.java:7648)
        clojure.lang.RT.loadResourceScript (RT.java:381)
        clojure.lang.RT.loadResourceScript (RT.java:372)
        clojure.lang.RT.load (RT.java:459)
        clojure.lang.RT.load (RT.java:424)
        clojure.core/load/fn--6839 (core.clj:6126)
        clojure.core/load (core.clj:6125)
        clojure.core/load (core.clj:6109)
        clojure.core/load-one (core.clj:5908)
        clojure.core/load-one (core.clj:5903)
        clojure.core/load-lib/fn--6780 (core.clj:5948)
        clojure.core/load-lib (core.clj:5947)
Caused by:
IllegalAccessError set-refresh-dirs does not exist
        clojure.core/refer (core.clj:4249)
        clojure.core/refer (core.clj:4217)
        clojure.core/apply (core.clj:667)
        clojure.core/load-lib (core.clj:5966)
        clojure.core/load-lib (core.clj:5928)
        clojure.core/apply (core.clj:667)
        clojure.core/load-libs (core.clj:5985)
        clojure.core/load-libs (core.clj:5969)
        clojure.core/apply (core.clj:667)
        clojure.core/require (core.clj:6007)
        clojure.core/require (core.clj:6007)
        sheluchin.user/eval67280/loading--6721--auto----67281 (user.clj:1)

tvaughan14:07:39

@UPWHQK562 This IllegalAccessError set-refresh-dirs does not exist seems to say to me that you're trying to compile a Clojure only (not available in Clojurescript) function into the javascript bundle. I don't use iced nor shadow's http server so I can't say how these need to be setup, but roughly speaking I have a server.core/run that starts the backend, and a user.core/run that I use for repl-driven development. user.core/run calls set-refresh-dirs and server.core/run, and user/core.clj is not in the source path. I run shadow separately in watch mode for development to hot reload Clojurescript changes, but static assets are served by a http-kit/ring backend

sheluchin14:07:14

@U0P7ZBZCK I seem to be getting inconsistent results after restarting things and reconnecting my editor to my REPL. Latest error looks like this:

[:main] Build completed. (309 files, 0 compiled, 0 warnings, 11.51s)
[2021-07-20 10:14:45.512 - WARNING] :shadow.cljs.devtools.server.nrepl-impl/init-ns-ex - {:init-ns sheluchin.user}
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling at (sheluchin/server/db/mounts.clj:17:3). #:clojure.error{:phase :compile-syntax-check, :line 17, :column 3, :source "sheluchin/server/db/mounts.clj"}
	clojure.lang.Compiler.analyze (Compiler.java:6808)
	clojure.lang.Compiler.analyze (Compiler.java:6745)
	clojure.lang.Compiler$InvokeExpr.parse (Compiler.java:3820)
	clojure.lang.Compiler.analyzeSeq (Compiler.java:7109)
	clojure.lang.Compiler.analyze (Compiler.java:6789)
	clojure.lang.Compiler.analyze (Compiler.java:6745)
	clojure.lang.Compiler$BodyExpr$Parser.parse (Compiler.java:6120)
	clojure.lang.Compiler$FnMethod.parse (Compiler.java:5467)
	clojure.lang.Compiler$FnExpr.parse (Compiler.java:4029)
	clojure.lang.Compiler.analyzeSeq (Compiler.java:7105)
	clojure.lang.Compiler.analyze (Compiler.java:6789)
	clojure.lang.Compiler.analyzeSeq (Compiler.java:7095)
Caused by:
RuntimeException No such var: mount/start
	clojure.lang.Util.runtimeException (Util.java:221)
	clojure.lang.Compiler.resolveIn (Compiler.java:7388)
	clojure.lang.Compiler.resolve (Compiler.java:7358)
	clojure.lang.Compiler.analyzeSymbol (Compiler.java:7319)
	clojure.lang.Compiler.analyze (Compiler.java:6768)
	clojure.lang.Compiler.analyze (Compiler.java:6745)

sheluchin14:07:10

My mounts.clj looks like this:

(ns sheluchin.server.db.mounts
  (:require
   [crux.api :as crux]
   [mount.core :as mount :refer [defstate]]
   [taoensso.timbre :as log])
  (:import (crux.api ICruxAPI)))

(defstate ^ICruxAPI crux-node
  :start (do
          (log/info "Starting Crux node")
          (crux/start-node {}))
  :stop (do
          (log/info "Stopping Crux node")
          (.close crux-node)))

(defn start []
  (mount/start #{#'crux-node}))

(defn stop []
  (mount/stop #{#'crux-node}))

(defn restart [] (stop) (start))

tvaughan14:07:22

Same-ish issue. mount is a Clojure not Clojurescript thing so can't be compiled by shadow

tvaughan14:07:44

$ cat shadow-cljs.edn
{:builds {:ui {:target :browser
               :output-dir "resources/public/static/js"
               :asset-path "/static/js"
               :compiler-options {:infer-externs :auto
                                  :optimizations :advanced
                                  :output-feature-set :es6
                                  :warnings-as-errors {:ignore #{com.cognitect.transit}
                                                       :warnings-types #{:undeclared-var}}}
               :devtools {:repl-pprint true
                          :preloads [example.ui.preload
                                     com.fulcrologic.fulcro.inspect.dom-picker-preload
                                     com.fulcrologic.fulcro.inspect.websocket-preload]
                          :after-load example.ui.client/reload!
                          :watch-dir "resources/public"
                          :watch-path "/public"}
               :module-hash-names true
               :modules {:main {:init-fn example.ui.client/init!
                                :entries [example.ui.client]}}}
          :test-runner {:target :karma
                        :output-to "resources/test/js/test-runner.js"}}
 :nrepl {:host "0.0.0.0"
         :port 5310
         :middleware [cider.nrepl/cider-middleware
                      cider.piggieback/wrap-cljs-repl
                      refactor-nrepl.middleware/wrap-refactor]}}

tvaughan14:07:17

In deps.edn under aliases:

:shadow {:extra-deps {binaryage/devtools {:mvn/version "1.0.3"}
                                 cider/cider-nrepl {:mvn/version "0.26.0"}
                                 cider/piggieback {:mvn/version "0.5.2"}
                                 com.lambdaisland/dom-types {:mvn/version "0.0.11"}
                                 refactor-nrepl/refactor-nrepl {:mvn/version "2.5.1"}
                                 thheller/shadow-cljs {:mvn/version "2.15.1"}}
                    :extra-paths ["test"]
                    :main-opts ["-m" "shadow.cljs.devtools.cli"]}

tvaughan14:07:55

This part of shadow-cljs.edn:

:modules {:main {:init-fn example.ui.client/init!
          :entries [example.ui.client]}}
is the likely culprit. For some reason whatever it is you have specified here includes set-refresh-dirs and mount

sheluchin14:07:02

All I have there is:

:modules       {:main  {:init-fn sheluchin.client/init}}
(ns sheluchin.client
  (:require
    [com.fulcrologic.fulcro.application :as app]
    [com.fulcrologic.fulcro.components :as comp]
    [com.fulcrologic.fulcro.data-fetch :as df]

    [sheluchin.application :refer [app]]
    [sheluchin.ui :as ui]))

(defn ^:export init []
  (app/mount! app ui/Root "app")
  (df/load! app [:note-list :singleton] ui/NoteList)
  (js/console.log "Loaded"))

(defn ^:export refresh []
  ;; re-mounting will cause forced UI refresh
  (app/mount! app ui/Root "app")
  ;; 3.3.0+ Make sure dynamic queries are refreshed
  (comp/refresh-dynamic-queries! app))

sheluchin14:07:06

I'll see if I can get some help for this in the shadow channel. Do you happen to know any good resources that would help me make sense of clojure tracebacks like what I've been posting? It seems like there is quite a bit of interpretation you have to apply and things aren't usually obvious from the logs.

tvaughan14:07:36

> shadow-cljs will follow the dependency graph from the root set of code entry points in the `:entries` to find everything needed to actually compile and include in the output. Doesn't say what the behavior is when this is omitted, but sounds relevant to what you've posted above

tvaughan14:07:50

> Do you happen to know any good resources that would help me make sense of clojure tracebacks like what I've been posting? I don't have any bookmarked, but https://8thlight.com/blog/connor-mendenhall/2014/09/12/clojure-stacktraces.html and https://www.cbui.dev/how-to-understand-clojure-stacktraces-and-error-messages/ might be helpful

👍 2
sheluchin14:07:46

[2021-07-20 10:36:26.857 - WARNING] :shadow.cljs.devtools.server.nrepl-impl/init-ns-ex - {:init-ns sheluchin.user}
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling var at (clojure/core/rrb_vector/fork_join.clj:7:1). #:clojure.error{:phase :compile-syntax-check, :line 
7, :column 1, :source "clojure/core/rrb_vector/fork_join.clj", :symbol var}
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7115)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.analyze (Compiler.java:6745)
        clojure.lang.Compiler$InvokeExpr.parse (Compiler.java:3888)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7109)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.access$300 (Compiler.java:38)
        clojure.lang.Compiler$DefExpr$Parser.parse (Compiler.java:596)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7107)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.analyze (Compiler.java:6745)
        clojure.lang.Compiler.eval (Compiler.java:7181)
Caused by:
RuntimeException Unable to resolve var: r/fjfork in this context
        clojure.lang.Util.runtimeException (Util.java:221)
        clojure.lang.Compiler$TheVarExpr$Parser.parse (Compiler.java:720)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:710

tvaughan14:07:02

This is after adding :entries?

sheluchin14:07:57

But I've seen it before as well, I'm pretty sure. Like I said, I seem to be getting inconsistent results :face_with_raised_eyebrow:

tvaughan14:07:51

I doubt the order of compilation is deterministic. This still looks like the same problem, shadow is trying to compile a Clojure/JVM only thing which it cannot do

sheluchin14:07:04

Thanks, @U0P7ZBZCK. Appreciate your input very much. I'm going to stop taking up your time on this one. Will take a break, read the two articles you posted, and see if I can get some help in the shadow channel.

tvaughan14:07:35

Perhaps try commenting out any references to sheluchin.application and sheluchin.ui in sheluchin.client just to see if that compiles. If it does, then somehow something is being required somewhere under (could be a require of a require of a require ...) sheluchin.application and/or sheluchin.ui which is clojure/jvm only

tvaughan14:07:23

That's my working theory anyway 😉

sheluchin16:07:57

Yeah, guess that's not the case. I also commented out the references in sheluchin.client but I'm still getting errors.

sheluchin16:07:37

I notice that making edits to my mutations.clj file does not result in a recompilation, and changes don't get reflected until I restart my REPL entirely. Maybe it's somehow related.

sheluchin16:07:20

Error is different yet again, this time it mentions Crux:

[:main] Build completed. (309 files, 0 compiled, 0 warnings, 11.31s)
Warning: resetting Nippy thaw for custom type with id: :crux.codec/edn-id
Warning: resetting Nippy thaw for custom type with id: :crux.codec/id
[2021-07-20 12:47:33.598 - WARNING] :shadow.cljs.devtools.server.nrepl-impl/init-ns-ex - {:init-ns sheluchin.user}
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling at (mount/core.cljc:1:1). #:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :source "mount/core.cljc"}
	clojure.lang.Compiler.load (Compiler.java:7648)
	clojure.lang.RT.loadResourceScript (RT.java:381)
	clojure.lang.RT.loadResourceScript (RT.java:372)
	clojure.lang.RT.load (RT.java:459)
	clojure.lang.RT.load (RT.java:424)
	clojure.core/load/fn--6839 (core.clj:6126)
	clojure.core/load (core.clj:6125)
	clojure.core/load (core.clj:6109)
	clojure.core/load-one (core.clj:5908)
	clojure.core/load-one (core.clj:5903)
	clojure.core/load-lib/fn--6780 (core.clj:5948)
	clojure.core/load-lib (core.clj:5947)
Caused by:
IllegalAccessError throw-runtime does not exist
	clojure.core/refer (core.clj:4249)
	clojure.core/refer (core.clj:4217)
	clojure.core/apply (core.clj:667)
	clojure.core/load-lib (core.clj:5966)
	clojure.core/load-lib (core.clj:5928)
	clojure.core/apply (core.clj:667)
	clojure.core/load-libs (core.clj:5985)
	clojure.core/load-libs (core.clj:5969)
	clojure.core/apply (core.clj:667)
	clojure.core/require (core.clj:6007)
	clojure.core/require (core.clj:6007)
	mount.core/eval70607/loading--6721--auto----70609 (core.cljc:1)

sheluchin15:07:56

Thanks for the feedback everyone. I'll take the time to understand mount and learn how to use it.