Fork me on GitHub

Hello there! Is there a way to get how a specific dependency is being pulled on shadow-cljs? Specifically, something like lein deps :tree or similar


there is a rough version in shadow-cljs info


Ah, ok, thanks. Turns out Pathom was being pulled by shadow-cljs itself 🙂.

parrot 3
Clément Ronzon18:11:22

Hi guys, I am developping a SPA using re-frame and I have a question about shadow-cljs + karma + coverage. In my edn file I have a :karma-test under :build which is like this:

:karma-test   {:target    :karma
              :ns-regexp "-test$"
              :output-to "target/karma-test.js"}
What I understand is that Shadow is going to compile all my cljs files, from src/ and test/ into that target/karma-test.js file. I'd like to report test coverage using karma's coverage plugin but for that, I need to have the sources and the test in separate files, right? If I add this to my karma config file, it runs into a loop:
preprocessors: {
            'target/karma-test.js': ['coverage']
Was anyone successful at running coverage report? (FYI I'm quite new at using clojure/clojurescript/shadow-cljs)

Clément Ronzon18:11:00

Here is the output of the "loop" I mentioned:

<--- Last few GCs --->

[743:0x2e177e0]    27020 ms: Scavenge 1365.5 (1423.5) -> 1364.7 (1424.0) MB, 2.0 / 0.0 ms  (average mu = 0.300, current mu = 0.273) allocation failure 
[743:0x2e177e0]    28513 ms: Mark-sweep 1365.6 (1424.0) -> 1362.4 (1423.5) MB, 1490.8 / 0.0 ms  (average mu = 0.392, current mu = 0.459) allocation failure scavenge might not succeed
[743:0x2e177e0]    28518 ms: Scavenge 1363.3 (1423.5) -> 1362.5 (1424.0) MB, 2.6 / 0.0 ms  (average mu = 0.392, current mu = 0.459) allocation failure 

<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x1246665bf1d]
    1: StubFrame [pc: 0x1246661b535]
Security context: 0x0e367139e6c1 <JSObject>
    2: builder(aka builder) [0x3a525ca3dcf1] [/app/node_modules/@babel/types/lib/builders/builder.js:~16] [pc=0x12466a63ad1](this=0x287b09c026f1 <undefined>,/* anonymous */=0x3841126a8a69 <String[14]: ObjectProperty>)
    3: arguments adaptor frame: 3->1
    4: valueToNode(aka valueToNode) [0x358fe6fb6809] [/app/node_modul...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x8fb090 node::Abort() [node]
 2: 0x8fb0dc  [node]
 3: 0xb031be v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
 4: 0xb033f4 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
 5: 0xef7452  [node]
 6: 0xef7558 v8::internal::Heap::CheckIneffectiveMarkCompact(unsigned long, double) [node]
 7: 0xf03632 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [node]
 8: 0xf03f64 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
 9: 0xf06bd1 v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [node]
10: 0xed0054 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationSpace) [node]
11: 0x117012e v8::internal::Runtime_AllocateInNewSpace(int, v8::internal::Object**, v8::internal::Isolate*) [node]
12: 0x1246665bf1d 
Aborted (core dumped)


sorry, don't know anything about karma/coverage stuff/

Clément Ronzon20:11:15

The only thing that is missing so coverage can run is to have the sources separated from the tests. Right now they are regrouped in the same file kamra-test.js. I would really appreciate if you could help me figuring out how to separate them please?


I do not understand what you mean by separate


I have not used karma coverage so I don't have the slightest clue how it works or what it expects


if you point me to some documentation I can take a look

Clément Ronzon20:11:37

Sure, this is the doc: In particular:

preprocessors: {
      // source files, that you wanna generate coverage for
      // do not include tests or libraries
      // (these files will be instrumented by Istanbul)
      'src/**/*.js': ['coverage']
We want the source files to be separate from the test files.


you can try using a :npm-module build but I have my doubts that changes much


but that has each ns is its own file

Clément Ronzon20:11:41

Ok thx, I will try!

Clément Ronzon22:11:01

Hi @thheller, I've spent a good amount of days trying and searching for alternatives, in vain. How feasible would it be to have the :browser-test target generating 2 js files? - one with only the namespaces from the sources - one with the tests and all the rest Having this, it would be extremely easy to run test coverage.


sorry I don't understand what that would accomplish

Clément Ronzon16:11:39

No worries, let me explain: The code that needs to be analysed, in terms of coverage, has to be in a separate file from the rest of the code (tests, libraries etc.) so we can specify it in Karma's configuration (`preprocessors: 'path/to/code/to/analyse/*.js': ['coverage']`) (see This way Karma's coverage "knows" what js files are to analyse v.s. what js files are testing that code. Currently, the :karma target dumps all the js code into a single file which makes it impossible to run the coverage module with any accuracy.


I don't understand the last argument


how does it get more accurate with multiple files? I presume it uses source map to map the code which will point back to the original sources?


why does it matter that there multiple and not one file?


please point me to technical docs explaining why if there are any?


I simply do not know enough about karma to make any technical decisions but what you are asking does not make sense to me in a build sense. I told you before to use :npm-module if you want one file per namespace. that is the best I can offer?


you can create a reproducible example repo with something that doesn't work so I can take a look


but so far I don't have enough information to make more suggestsions

Clément Ronzon16:11:25

In the section "Preprocessor"

Clément Ronzon16:11:40

There is a BAD example

Clément Ronzon16:11:45

> In this example also JASMINE and JASMINE_ADAPTER get included but they shouldn't as these file are only for the test setup used and not for your program.

Clément Ronzon16:11:03

> If you include these files there can occur side effects like the following, > - a part of the code coverage report will be output in the installation directory of Karma. > - the code coverage rate is reduced unfairly.

Clément Ronzon16:11:08

I understand you must have bigger fish to fry and I'd love to be able to propose a PR.

Clément Ronzon16:11:49

I cloned your repo locally but I hadn't enough time to put myself in the code in order to understand where to tweak it.

Clément Ronzon16:11:12

Please, if you could point me into the right direction I would appreciate it!


the only thing I can say is that I need a reproducible repo to actually see what is happening before making any decicions


the JASMINE line makes so sense to me since that is not something shadow-cljs uses or includes

Clément Ronzon16:11:26

I took it as an example, it's valid with any non-functional/production code


I can't point you in any direction since I still don't have the slightest clue which problem you are actually trying to solve

Clément Ronzon16:11:01

OK. Let me create a repo to illustrate the issue

Clément Ronzon16:11:13

I'll get back to you in a few days

Clément Ronzon17:11:48

It was faster to demonstrate than I thought: Let me know if I can help on anything 🙂


ok from what I can gather you are expecting to get 100% coverage from running one simple test?


but since the code includes all of cljs.core and so on that skews your numbers and you only want coverage for you actual ns


can't think of a decent way to make that happen


thats the code controlling the output. so you can try and modify it to match your needs.

Clément Ronzon19:11:26

Thank you Thomas! I'll look into it.

Clément Ronzon23:11:57

Ok, I found a way to make it work! I know it is a bit ugly... 😁 I created a remediation branch on the repo to show how I made it work ( @thheller I'd be very happy to see this change included in the next release of shadow-cljs, if possible. Please let me know how can I help with that.


you can rename the karma.clj to karma2.clj or move it to an entirely different namespace


you can then publish it as a separate library


as a quick test I made

File                  | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
All files             |   71.43 |     62.5 |      50 |      72 |                 |     100 |      100 |     100 |     100 |                   
 coverage.app_test.js |   65.22 |       50 |   33.33 |      65 | 3,9,14-15,22,26-27


that seems more along the lines of what you want? actually split by ns? but for some reason it just reports coverage and no longer failing tests


anyways I don't really use karma myself so someone that does should really work on this more

Clément Ronzon16:11:31

Good morning Thomas. TY for looking into it. For your test: have you pulled the remediation branch or did you just put the override of karma.clj? (I'm asking because it looks more like a configuration thing)


basically the idea was to generate a files.json file so karma knows how to load the separate files in order


so the files are truly seperate


seems to work but probably requires tuning a lot more

Clément Ronzon16:11:41

TY, I like this approach. I'll dig into it and let you know if I can tune it.

Clément Ronzon16:11:39

Would it be a good idea to reference karma.clj's function that hasn't been changed instead of redefining them? I'm thinking that it would be good to have both targets evolving along but at the same time it would make it harder to maintain.


no clue. depends on how many other changes make sense I guess.


I built the karma stuff because someone asked how to do it ... without ever having used it myself there are probably a lot of things that could be done better

Clément Ronzon17:11:56

How did you configure the target in your shadow.edn please?


no change from yours


except :target :karma2 of course

Clément Ronzon17:11:42

If you change this in your karma.conf.js you'll get a better result:

preprocessors: {
            'target/files/coverage.!(*_test).js': ['coverage']

Clément Ronzon17:11:07

Basically it prevents coverage from analyzing the test code.

Clément Ronzon17:11:57

I noticed that the / are escaped in files.json:

Is there a way to prevent that from happening ?

Clément Ronzon17:11:19

found it: (json/write-str filenames :escape-slash false)

Clément Ronzon21:11:29

Currently there is no mapping produced by the :karma2 target. How difficult would it be to include it?


that flushes the sources with source map

Clément Ronzon22:11:00

Am I missing something? The "file" entry is omitted from the source map:

  "version": 3,
  "sources": [
  "names": [
  "sourcesContent": [
    "(ns )\n\n(defn some-function [arg]\n      (if (nil? arg) \"foo\" \"bar\"))"


what would that do? the content is right there?

Clément Ronzon22:11:13

You are right, this field seems to be optional ( I think there is something I missed with the config of remap-istanbul plugin. FYI this plugin allows a mapping between the js files and the cljs sources.

Clément Ronzon22:11:09

Yeah, that's it, I have an outdated version, this has been fixed:

Clément Ronzon23:11:08

After a bit of fighting with this abandon-wares I finally got it to work:

File       |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |
 coverage/ |      100 |      100 |      100 |      100 |                |
  app.cljs |      100 |      100 |      100 |      100 |                |
All files  |      100 |      100 |      100 |      100 |                |

Clément Ronzon23:11:26

Thanks a lot for your help @thheller!!


@thheller I want to improve the ability to switch between repls when working on a browser extension and contribute the improvements back if I get anywhere. Can you give me a push in the right direction? For a first step, I was hoping to set things up so I can iterate on shadow while exercising it interactively from my main project. Guessing :cli or :create-cli builds are my best bet?


not sure what you mean by :cli or :create-cli?


and what do you mean by the "ability to switch between repls"?


technically the setup expects the editor to do the switching but none is really doing it currently. still need to do a proper writeup of how to do it I guess.


Those are builds in the shadow-cljs repo, I assume they output the npm installable command line tool? Guessing there's also a way to load up and interact with the library code directly?


I've written some code over shadow/repl-runtime-selectto help me switch between repl environments, but the selection is brittle (it depends on the order they start in to know which one I'm picking) and as the runtimes come and go (eg page reload) it doesn't seem like the connection gets maintained consistently, though I could be wrong.


:cli is the code for the shadow-cljs command yes. I do not understand what you mean by "way to load up and interact with the library code directly?"


which library code?


the repl-runtime-select is never going to be pretty either way since hacking this together over nrepl is always going to be limited by nrepl


just to clarify: what you call "repl environments" I call "runtimes". will make it easier in communication if you use that word please 🙂


and yes the editor is supposed to pick the runtime it wants to eval in


or I guess repl client, not limited to "editor"


if you open the shadow-cljs UI it will list all connected runtimes under http://localhost:9630/runtimes


the "vision" is that any client also lists those and you just click whichever one you want to talk do


the repl-runtime-select thing is pretty much dead and gone. it doesn't do anything at all anymore these days.


as you say it was very brittle anyways


do you have a plan for how to make it more reliable? I mean I'd do it myself but I can't come up with a cleaner way to do things that don't require changing how the editor handles/presents the REPL interface