-
-
Notifications
You must be signed in to change notification settings - Fork 1
Description
#The user namespace is the default used by the REPL. Adding a custom user.clj file that defines a user namespace automatically loads (reads & evaluates) all the code in that user.clj file.
This is a common practice in Clojure and can be very effective way of loading in tools useful for improving the Clojure development experience
The user namespace should not be used as part of the application code or have anything related to run ing any code that goes into production
user namespace is not loaded at the very end of starting the Clojure runtime, so setting dynamic vars may not be possible (TODO investigate startup order of Clojure and what is not possible in a user.clj file)
A custom user namespace can cause problems and even prevent the REPL from starting
Tips and constraints for using a user.clj
Alternative: load a specific file
clojure -M:project/build -i build.clj -r to start a REPL in "build" mode.
-i loads build.clj as an "init script", -r signals that a REPL should be started.
REPL starts in the user ns with build.clj loaded, allowing use of (build/database-setup {}) or (build/run-tests {:projects '{api auth login]})
Keep user.clj seperate
The custome user namespace should never be part of the default classpath for a project and should not be packaged in a jar or uberjar
Never require the user namespace from other namespaces, consider it a top-level namespace that may refer to other development only namespaces (TODO: environment namespace example)
Create a dev/user.clj and include it only when doing local development
TODO investigate how to keep dev/user.clj separated in Leiningen
Packaging apps
TODO investigate packaging apps with Clojure CLI
Assuming user.clj is on a dev profile classpath, then ensure it doesn't get included by using the production profile, which excludes all other profile configuration
lein with-profile production uberjar
There should be only one user.clj
Clojure looks for the first user.clj file on the classpath during runtime initialization. So if there are multiple ones, which one it finds first is entirely dependent on the order of your classpath. With clj, you can see your classpath with clj -Spath. The local project paths are always placed first (followed by your dependencies). Generally, I think it would be bad for a library to publish with a user.clj, so you should only find them locally.
If you always want to find some special one first, you should ensure it’s in the first path in your source :paths.
require expressions
Limit the number of required libraries
-
more libraries (and there dependencies) increases startup time and increases risk of conflict. Use minimum amount of libraries and consider excluding transitory dependencies
-
requiring-resolve to load on demand (TODO find out more about this) and optionally load dependency namespaces only if they are on the classpath
-
https://blog.flowthing.me/clojure-repl-start/ require-resolve examples
Reload code
When reloading code with integrant, mount, etc ensure directories like dev are included so the custom dev/user.clj is found on the classpath
(clojure.tools.namespace.repl/set-refresh-dirs "src" "test" "dev")
Constraints due to Clojure startup order
Dynamic vars have to be bound at the root before you can rebind them dynamically. The repl binds a bunch of dyn vars for you but user.clj is loaded before the repl
You could alter-var-root, but the repl is going to rebind over you (the default is actually false - the repl rebinds it to true)
There is not currently an easy affordance to change this, but you could start your own repl and do whatever you want in it
https://insideclojure.org/2020/02/11/custom-replan
https://insideclojure.org/2020/02/11/custom-repl/
TODO some things to try to test startup order of Clojure and user.clj
(set! print-namespace-maps false)
(defmethod print-method clojure.lang.IPersistentMap [m, ^java.io.Writer w]
"From https://clojuredocs.org/clojure.core/*print-namespace-maps*"
(#'clojure.core/print-meta m w)
(#'clojure.core/print-map m #'clojure.core/pr-on w))
Lint and LSP
disable warning for unused imports? have user.clj with:
(ns user
(:require [integrant.repl :refer [clear go halt prep reset reset-all]]
[integrant.repl.state :as state]))
, which causes (for all of the unused ones):
... warning: #'integrant.repl/clear is referred but never used
Clj-kondo config
{:config-in-ns {user {:linters {:unused-referred-var {:level :off}}}}}
References
Metadata
Metadata
Assignees
Labels
Type
Projects
Status