Something I've been meaning to bring up. shadow-cljs has this functionality for test builds where it scans the filesystem for test namespaces, and implicitly makes them dependencies of a main namespace (a test runner ns). This makes sure not just that they are included in the build, but that they are compiled before the runner, so that when the runner compiles it can inspect the compiler env to find all tests. This makes it possible for a third party runner like Chui to exist, where people can just point at a runner ns that's provided by the library, instead of having to write their own where they manually require all their tests. I brought this up with @bhauman and he's considering if it makes sense to have something similar in Figwheel, but having it directly in the compiler so it's available regardless of the tool you're using would be even better. Thoughts?
this sounds like a divergence from Clojure? You have to require the test nses for run-tests
to work
also I don't really understand the "manually require" - the test runners that don't require analysis stuff just support a simple naming pattern matching which works just fine.
so I don't see what the gain is for end users - it's already easy right now - but maybe I missing a subtle point
the problem doesn't exist in clojure since the test-runner can just dynamically require all the tests it wants to run
in CLJS the burden is on the user to create one namespace that requires all the other test namespaces
so in shadow-cljs instead there is a generic test runner ns everyone can re-use and the requires are just provided by the build
yes I understand the dynamic require thing - but there is no such thing as a test-runner in Clojure
this is a tooling thing
a test-runner implies a lot more than just dynamically requiring - which implies you have to know what look for anyway (filtering)
that is not the topic here
but regex based matching a la cljs test runner accomplishing the same thing
it is
the conversation is about including something
and it has to be weighed against existing easy stuff
also the above is really problematic in that doesn't elaborate
so in shadow-cljs how does a user filter?
or control what sets of tests - or run a specific test
:ns-regexp "-test$"
, and a couple more options. can also specify by name manually
right again this all smells of affordances
not something that Clojure does or cares about
thus probably not something we should care about
other than making it possible for someone else to solve the problem
I think thats what the initial question is about. I think currently this isn't very easy to do.
assume that the test tool already filtered and found all the namespaces it wants to test
how does it tell the compiler to compile those before compiling the additional "test-runner" itself?
ie. if the test-runner doesn't have the requires itself parallel-build may just decide to compile it whenever without actually waiting
I'm even more confused now by not easy
the compiler can compile forms, you can create test runner in memory and pass it into build no?
sorry I don't really know what the state of the official APIs for this is. just wanted to make clear why this is useful to have from the users perspective.
gimme one second, browser REPL has an example
https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/closure.clj#L3397-L3411
yes the compiler can take source forms directly
that should be enough then
does this allow me to solve this for my users in a way that is tool agnostic?
probably no because neither shadow-cljs nor figwheel are "in the loop" for that build path
I'm trying to imagine how this would work in practice, say someone is using cljs.main --compile
, what do I tell them?
cljs.main
doesn't need to be involved, we're talking about writing a test runner easily.
build can take a series of ClojureScript forms in memory
you don't even need to code gen (to disk)
you can fabricate the test runner
you can set :main
there's nothing else
yes, I can invoke the compiler myself. I get that. I don't want that. People have their own setup with their own tools, they figured out how they're dealing with dependencies and externs and whatever. I just want to add something to their build.
kaocha-cljs currently uses repl-env
which means we implicitly invoke the compiler, this leads to no end of trouble because we don't replicate people's actual setup
this is going into the weeds about how you would like it work π
I'm saying it works right now, maybe not how you want it work but it is already trivial to write a test-runner
adding test running features to cljs.main
is not going to happen
... but
I just don't understand what your concerns are
clj -m my.cool.test-runner-tool -co build.edn --run-tests
how does this not mean you can use your own tools, deps, etc.?
so my.cool.test-runner-tool
knows about shadow vs figwheel vs cljs.main?
huh?
why does it need to know about those things
if something has to know about those things - something is very, very wrong
all those tools already taking different options that are different from cljs.main
so there's nothing to reconcile
kaocha-cljs currently doesn't work for a lot of shadow users because shadow's compilation is subtly different. that's why we want to get out of the job of invoking the compiler
I don't see how ClojureScript can solve this problem - the build tools are incompatible
I don't see how this is different from Lein, Boot, deps.edn
the thing is I can write a library and people can include it using lein, boot, deps.edn. I'd like to write a test runner that people can include with shadow, figwheel, cljs.main
but there is one aspect that I can't handle as a library
and that brings us back to the top
let's rewind a bit
because I do not understand now what you are saying
ok... let me try to rephrase or explain better the use case
well first let's elaborate what I see the issue w/ what you're saying is
ok, sure
and maybe you can tell me how you could solve it
to me you can't make a generic thing that works with all these build tools because they might inject something tooling specific
ClojureScript can't possibly know about these tooling specific things
you could write a library to create test-runner namespaces sure (but this is like ~100 lines of code for the basics)
but every tool actually needs to integrate that
and maybe they want to customize it
so a library doesn't appear to me to offer anything
the other option - which is perfectly fine and it's what I currently do is not care about these tooling in my testing scenario since these inevitably ends up as CI things
so who cares about shadow, figwheel, etc.
you just going compile w/ your test-runner and run Node.js or Puppeteer
right, so that's all fine, I think we're getting off track here. I don't care about these tools apart from the fact that they can compile ClojureScript. They may have their own testing features, that's not what I'm concerned with.
consider this macro https://github.com/lambdaisland/chui/blob/master/modules/chui-core/src/lambdaisland/chui/test_data.clj#L50-L53
based on this chui offers an API for dynamically invoking tests. That API is used in browser testing UI, as well as in a component that gets tests to execute and reports on the results via a websocket
this is all just standard ClojureScript, you just add it to your build and start it with a browser or node or whatever, we don't care.
this seems REPL-centric?
but we can only know about the tests that have been compiled by the time that macro runs
since you're reflecting?
is it?
currently there are two ways this can work, either you do
(ns foo (:require <all-the-tests>))
(capture-test-data!)
or you let shadow add those requires for youmaybe there are better ways to solve this.... I don't know. As Thomas pointed out we lack the dynamic require so we can't scan the filesystem the way we do in Clojure, making it harder to provide a similar user experience
Some thoughts
it maybe we're missing something small - you need some dep graph for capture-test-data!
to find/filter on
the REPL has a feature :analyze-path
which just analyzes everything in a directory
this could be generalized to feed something like that macro
this also happens when you use a directory as a build input
finally you need require
outside of ns
but we already added that
@bhauman right good point
@plexus so that's probably enough a directory for build, and emitting require
I did think of that but assumed I wouldn't be able to emit a require from a macro. Does that work?
we could probably make it work in (do ...)
if we didn't already do that
the scan part is still missing?
like scanning for tests? analyzer apis should work here - not sure about doing test specific stuff unless Clojure has something similar
cljs.analyzer.api
alright, this is promising. I can experiment with that.
@plexus if do
doesn't work then a patch is welcome, I don't think this will be very hard
π
@plexus but also I don't think you need it?
(require ns-spec ...)
right
you can have N
https://ask.clojure.org/index.php/9334/satisfies-produces-inference-warning-given-unhinted-argument
my gosh getting simple re-bundle on npm_deps.js file change is much harder than I imagined, but almost done
What's the complexity? Curious more than anything :)
its mostly with extra-mains and stuff
had a simple-stateful file-changed function and it was failing because well its stateful
and a single npm_deps.js file is shared between the build and an extra-main build
so hard to detect a change twice
I see, so the state of bundle is external.
Hmm, maybe you just need to check if the bundle is newer than the npm_deps.js?
nope π npm_deps.js gets output on every run!!
so you may start to see these problems accumulating
so I have to have a checksum ie hashcode of the contents
Ah! So you'd need to do a contents based hash then.
yep
yeah I thought this would take an hour, you know the story
If you don't know, there's a crc32 implementation built into the jvm. It's fairly nice to work with.
now Iβm having to store that hash in a scoped manner
actually (.hashCode ) works just fine
not perfect but Iβve never seen it fail
I guess it's short and if you're slurping it... I've used crcs with very big files
yeah main.js and deps are both small files
But I have 10,0000 npm deps!
not in npm_deps.js you donβt π
right????
its only the ones you require directly
Haha. The fact you put right??? Let's me know you'd have believed me if I'd said that's how many I directly required.
hey weβre developers, anythings possible