Fork me on GitHub
#shadow-cljs
<
2023-02-23
>
alexdavis15:02:09

Possibly I'm not seeing something obvious here, but I have a namespace with no require

(ns cljs.globals)

(goog-define ENV-NAME "NONE")
and my entry point as defined by :modules {:app {:entries [cljs.core]}} requires it
(ns cljs.core
  (:require
   [cljs.globals :refer [ENV-NAME]]
....
yet I'm somehow getting this when I try running shadow
Circular dependency detected: cljs.core -> cljs.globals -> cljs.core
even though globals is not requiring anything.. any ideas?

pesterhazy15:02:41

Is it because the name cljs.core is special? Try renaming to mycljs.core

alexdavis16:02:56

Ah yes! I don't know why I didn't think of that... I have js and cljs files in the project so named the root folder cljs, but that was probably a bad move. Renaming fixes it

thheller16:02:26

yeah, replacing cljs.core is not a good idea 🙂

zimablue17:02:00

just dug to the bottom of a problem, having a lot of cljc files but not using java other than compilation and macros, then adding a :require-macros on a file which imports a lot of other files seems to force the clojure part to activate and the compiler "notices" that my cljc files aren't valid, having js/blah and other clojurescript-only things. It's like there's this kind of bomb in my code waiting for me to make a high level file macro-self-referential

zimablue17:02:15

because the errors only come up intermittently between big changes, it means that the "space" I have to search for the problem and fix is big, and also these sort of cljc compilation errors often don't give the file/line that explodes, which overall makes it something that causes pain every now and again

zimablue18:02:01

this last one took me doubly long because it seems that requiring a defrecord or defitem by name is valid clojurescript but not valid clojure:

(defrecord MyRecord [a b c])
 (:require [my-ns :refer [MyRecord]])
afaict works in cljs but not clj, so this part of the carnage took me ages to find by binary search

zimablue18:02:02

high level file in the sense of importing lots of other files, because failing under clj is transitive to files-importing so I basically have to search the space of all imported files for the problem

zimablue18:02:35

one technique which might be superstition is to put (:require-macros [this-ns :refer [dummy-macro]]) #?(:clj (defmacro dummy-macro [] nil)) into files that I consider suspect to force the underneath-clojure-tree to be checked or at least that file itself

zimablue18:02:06

sorry I don't have a specific suggestion and this also might be obvious or stupidity on my part but maybe my pain and ignorance is useful somehow

thheller20:02:41

why write .cljc files if you don't want the CLJ side?

zimablue21:02:34

I thought it was a necessity to put macros in the same file

zimablue21:02:38

It also kind of seemed like good practice, to keep code minimally seperated from clj in case part of the code was later exposed that way, but a combination of the way that it's lazy in checking the clj half works, rough non-file-specific error messages and some pitfalls I didn't know like the record import incongruity mean that this is maybe the third time it's cost me quite a while effectively doing a slow, semi-blind search through the code for cross-compilation problems

zimablue21:02:10

if I'm right that self-requiring any macro forces a check that all recursively required files work under clojure then I can do that at the app entry point in future to catch problems (earlier === in a reduced search-space) but I just thought I'd mention it here since it's maybe the most painful thing I've found in cljs development recently

thheller07:02:39

it doesn't force a check. it loads all the referenced files AS Clojure, which I guess implies a check but it is just regular clojure.

Daniel Leong22:02:27

Is there a reason cljs.test wouldn't be available from the repl connected to a shadow-cljs watch process for a :react-native build target? I'm used to being able to run tests for cljs at the repl, but I'm getting TypeError: undefined is not an object (evaluating 'cljs.test.report')

Daniel Leong00:02:18

Ah I added cljs.test to {:devtools {:preloads [cljs.test]}} and that seems to have fixed it!

Andrew Lai23:02:36

I’m seeking help debugging discrepancies between the shadow watch behaviors I’ve observed on two different OSes (I’m on a Mac M1, and my colleage is on WSL2). Our current hypothesis is that watch treats symlinks differently on Mac and WSL. Observations: When I run shadow watch on my Mac, Shadow runs the build and only re-compiles the CLJS files when I change a file in the src-paths directory. In contrast, when my colleague runs the shadow watch command on his WSL machine, his build loops indefinitely - it will finish compilation and then recompile (in a never-ending loop). The endless cycling behavior in WSL is associated with creating soft symlinks during our build process. Normally, the build emits compiled Javascript into a folder called .compiled-stories and then it symlinks the .compiled-stories folder to another location inside the src-paths directory. When we stop the build from creating the soft symlink of the .compiled-stories directory, the WSL machine does not loop indefinitely. Right now we hypothesize that the shadow watch (or the code it relies on, or WSL itself) somehow treats the WSL and Mac symlinked folders differently. Specifically, it looks like the https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/util/FileWatcher.java#L34-L46 creates different watchers on Mac vs Linux, so we’re hypothesizing that that could be a factor. Before we start to explore the differences between FileWatchers, we were looking for a sanity check: is there way we can determine why shadow triggered a recompile and trace the causal chain of events leading to a recompile?

Andrew Lai00:02:47

It seems like the MacOs Filewatcher library https://github.com/gmethvin/directory-watcher/issues/30. And the shadow FileWatcher class seems to use https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/util/FileWatcher.java#L77 in the call to registerAll - https://docs.oracle.com/javase/8/docs/api/java/nio/file/FileVisitOption.html`FOLLOW_LINKS`https://docs.oracle.com/javase/8/docs/api/java/nio/file/FileVisitOption.html. Is that line of code telling the file watcher to watch all symbolic links if possible?

Andrew Lai01:02:27

I have a Linux machine and home and I just tested this hypothesis on a mac and a Linux machine with a minimal example. I saw that, when running shadow watch, the Mac ignored it when I created new files in the symlinked directory, and the Linux machine did not ignore and tried to rebuild

Andrew Lai01:02:57

I’ll write up a minimal reproducible example and file an issue in Shadow

thheller07:02:47

I don't quite get what you mean by symlinking? who is symlinking what and when?

thheller07:02:18

it should generally be avoided to put output files somewhere shadow-cljs "watches", as that just creates a lot of useless noise. output files should never be inputs.

thheller07:02:47

so never put an :output-dir into something that is also in :source-paths or :paths

thheller07:02:23

if you create symlinks in any of your :source-paths that can indeed confuse things a lot

👍 2
Andrew Lai13:02:05

Re: symlinking: Who: Our custom-rolled build process is creating symlinks When: The links are created before we run shadow watch. But the links place the folder with the compiled Javascript into the :source-path.

Andrew Lai13:02:19

Here’s an illustrative example of what our directory hierarchy looks like

->myproject
 ->output-js (where output goes)
 ->src (being watched)
  ->foo.cljs
  ->output-js (soft symlink)

Andrew Lai13:02:54

I think the surprising this was that doing this was OK in MacOS; it seems like it only caused strange behaviors in Linux. I believe that is due to discrepancies in how the file watchers for Mac and Linux treat symlinks.

Andrew Lai13:02:43

I know that our build system is doing something a bit crazy by symlinking the output directory back into the watch path. But aside from that issue, I suspect that the MacOS file watchers would ignore any directory that is symlinked into the watch path. And i suspect that we would follow symlinks on a Linux machine.