cljs-dev

john 2024-11-19T06:26:48.247069Z

So I have a rough impl of a websocket server. I originally built it in a fork of clojurescript. After revisiting it recently, I realized I could pull out those files into a lib and everything would still work, like clj -M -m cljs.main -re ws -w src. And it stays pretty simple because it leans into Clojurescript's existing, builtin repling capabilities. I'm mostly making it for flexible websocket repling but it might be useful for other things too. I licensed it as EPL, so we could fold it back into Clojurescript one day if it polishes up nice. https://github.com/johnmn3/repl.ws

1
john 2024-11-23T22:03:14.771349Z

Okay so here it is more split out into it's own namespaces mink.mainhttps://github.com/johnmn3/mink One issue I'm still trying to work through is finding a way to pass some options like :prompt through from the cljs.cli interface. It's easy from the cljs.repl/repl* entry point, which is fine for socket-repl/prepl. But for the main entry I'd like to lean into cljs.cli as much as possible

dnolen 2024-11-24T01:20:14.908199Z

huh that might be a gap - I don't think I really thought much about custom prompts.

john 2024-11-24T01:57:16.408959Z

I'll just use repl*

dnolen 2024-11-27T17:55:03.063329Z

hrm I'm still confused - https://github.com/vouch-opensource/krell/blob/master/src/krell/repl.clj#L271

dnolen 2024-11-27T17:55:36.993329Z

fast initial prompt is a "root" level option far as I now - same as prompt - but maybe I'm missing something that you're saying about why this doesn't work

john 2024-11-27T18:19:53.598909Z

Well, my theory is that because repl/fast-initial-prompt? does not exist in this map: https://github.com/clojure/clojurescript/blob/9696bac5bd47abbc8741631989448db48ee017db/src/main/clojure/cljs/repl.cljc#L1078 It's not getting overwritten by that map, since that map comes after the other two in the merge-with (though the nil work in that merge-with maybe is preventing that, not sure), but because some keys like :prompt are in that third map, keys in the prior maps are maybe getting clobbered. I guess the merge with strategy is supposed to prevent that particular clobbering?

john 2024-11-27T18:21:47.311479Z

Yeah, maybe I was wrong about that, with the merge strategy there. I'll fire up a cljs branch and test that path

john 2024-11-27T18:24:46.059899Z

Oh wait, maybe not. Cause :prompt becomes repl-prompt in the opening binding form of the defn for repl*, in the :or part

john 2024-11-27T18:25:03.432749Z

So it's not nil, so repl-prompt always wins

john 2024-11-27T18:40:39.067599Z

oh wow, looking at krell, I didn't realize -re could take a namespaced foreign repl-env

john 2024-11-27T18:41:08.193809Z

Yeah I should have studied krell more. Lots of good stuff in there

dnolen 2024-11-28T02:48:59.836069Z

hrm I don't follow, repl-prompt only wins if it isn't passed in. But can pass it in via IReplEnvOptions

dnolen 2024-11-28T02:50:20.079729Z

(again I could be misreading, but I'm feeling like it should work just fine and has been accounted for)

john 2024-11-28T03:00:42.377469Z

Aren't the repl env opts coming in via repl*'s first arg, repl-env? Or do they come in via the second opts map? I thought they were coming in via repl-env, which gets parsed by -repl-options and then is merged with the :prompt that came from opts. Are you saying the IReplEnvOptions get passed in via the right (second) opts param to repl*?

dnolen 2024-11-28T03:14:48.590299Z

they have to be, otherwise my point about fast initial prompt wouldn't work

dnolen 2024-11-28T03:14:52.952949Z

in Krell

john 2024-11-28T03:16:56.008109Z

Oh okay. I misread it

dnolen 2024-11-28T03:17:46.478369Z

IReplEnvOptions has a docstring about this

dnolen 2024-11-28T03:18:07.128379Z

(which is cool, cause I didn't remember if we had been smart enough to do that 🙂

dnolen 2024-11-28T03:18:43.901229Z

it was designed specifically for the case where you need to declare the options for the REPL

john 2024-11-28T03:21:12.298969Z

well, fast-initial-prompt get's pulled off renv here and put on to opts as the second arg to repl*?

john 2024-11-28T03:22:08.009129Z

Well, I gotta trace it with the codebase running

john 2024-11-28T03:22:17.021959Z

But I thought I traced it before

john 2024-11-28T03:22:36.460529Z

But I'll chase it down tomorrow

dnolen 2024-11-28T03:33:38.225079Z

I think when I made that work I wasn't careful enough - there's no reason to limit it to fast initial prompt

dnolen 2024-11-28T03:38:37.666429Z

yeah I think is just a gap - https://clojure.atlassian.net/browse/CLJS-3423

dnolen 2024-11-26T14:15:50.826839Z

hrm did you look at IReplEnvOptions ?

dnolen 2024-11-26T14:16:18.043209Z

(not saying that's obvious at all, just asking whether you did)

john 2024-11-26T14:25:23.660479Z

@dnolen I ended up going with an api that https://github.com/johnmn3/mink/blob/06340632fa99051c298c360a9d2b83ca072165ec/src/mink/main.clj#L229. Oh, hmm, I may have messed up with that but I thought it wasn't working for my use case. I'll try again with that though. With my current impl I'm just taking all options. I just need to figure out which ones to split into compiler opts vs repl opts vs other opts and passing them down the correct path. Starting to lean on cljsc/known-opts for the compiler opts, But if there's an obvious way to split them up using existing filters, I thought I'd come here and ping you if anything comes to mind. WRT IReplEnvOptions... Oh, cli/main takes it as the first arg. Hmm. I'm not remembering why that didn't work... I'll check it

john 2024-11-26T14:34:44.981879Z

Maybe I couldn't figure out how to allow user inputs to flow down into my specific repl-env 🤔 I'll dig in and try to figure out what the specific issue was

john 2024-11-26T14:39:22.261719Z

IIRC, things like :prompt were not taking effect, when fed in from the IReplEnvOptions, but that could have just been user error

john 2024-11-26T16:38:27.397459Z

So reverting back to here and adding a :prompt fn right here: https://github.com/johnmn3/mink/commit/399d97f1ee341013cb3847720d7dae3964b005dc#diff-9f72dc7c40300c6abc33e2482a28f65590fa016720a5bb5d26a6996a43f58d6cR807 (in mink.main/repl-env*) I'm not seeing that the prompt fn is getting picked up and used. Prompt should be mink=> but it's still cljs.user=>

john 2024-11-26T16:40:20.998109Z

Maybe it needs go under a particular key in the repl-env?

john 2024-11-26T16:44:05.108939Z

wait...

john 2024-11-26T16:46:58.280009Z

Yeah, no mas. In both:

repl/IReplEnvOptions
  (-repl-options ...
And the merged in opts:
(merge (BrowserEnv.)
    {:host host
     ...
with a prompt of:
:prompt (fn [& args] (println :args args) (print "mink> "))
The prompt does not update

john 2024-11-26T16:50:36.025109Z

Looking in cljs.repl, it looks like :prompt is brought in via the opts (second param) and not the repl-env (first param):

(defn repl*
  [repl-env {:keys [init inits need-prompt quit-prompt prompt flush read eval print caught reader
                    print-no-newline source-map-inline wrap repl-requires ::fast-initial-prompt?
Does that mean the opts will clobber any :prompt passed in via the repl-env?

john 2024-11-26T16:56:38.826539Z

Again I think I could probably just add "-ro" "{:prompt blah ... to the cli/main fn call. Just seems hacky that way. And getting the whitespace to work going from clj to cli and back was brittle - never actually got it working, but I'm sure it's doable.

john 2024-11-26T16:59:00.750699Z

And I'd have to not clobber a user provided -ro so that's complicated

john 2024-11-26T17:10:16.711089Z

cli repl opts probably should over-ride default repl-env opts. But in this case I believe even nils are overriding the repl-env for some of these keys

john 2024-11-26T17:11:17.982789Z

Which isn't the end of the world - I can use repl* and that certainly aligns behavior more-so with socket repl/prepl I guess. So I'm not sweating it

dnolen 2024-12-02T13:44:07.625989Z

cool! if you could put together a patch that would be great - happy to see this fixed up.

john 2024-12-02T15:11:30.685549Z

Okay, I've been working on getting mink more aligned with working with web workers, which was my original impetus to mess with it in the first place. So I want to make sure my own requirements sussed out before requesting any changes. I should have a patch in this week.

👍🏽 1
john 2024-11-28T14:42:20.584139Z

Hmm, okay. I do think it's right that cli provided --repl-opts should override default repl opts... Even when a custom repl-env provides different repl opts. And people might be depending on that precedence. Seems like it might be a probably-non-breaking-change if opts continues to take precedence over repl-env, unless 1) repl-env populates one of those options and 2) user provided opts are nil anyway for those options.

john 2024-11-28T14:45:42.932689Z

So like, only when a custom repl-env doesn't provide a :prompt and the user doesn't provide a :prompt in their -ro opts or whatever else, then :prompt should default to repl-prompt

john 2024-11-28T14:45:59.074829Z

And that seems pretty minimal of a change

dnolen 2024-11-28T15:19:47.648719Z

I mean users supplying prompt via command line wasn’t a design goal - so I’m not super concerned. But yeah we could take some care to not break the small number of folks doing that

👍 1
john 2024-11-29T15:21:39.322259Z

Here's two takes on a possible patch. One is probably too naively simple: https://github.com/johnmn3/clojurescript/compare/master...johnmn3-small-code-change And this second one is probably more surgical, behavior-wise, but it's pretty messy and could probably be refactored into something more sensible: https://github.com/johnmn3/clojurescript/compare/master...johnmn3-bigger-change Gonna hammock those for a bit and look at it again later. Maybe a cond-> might clean it up a bit

dnolen 2024-11-24T15:46:56.706319Z

I read the cli code - I'm wondering what the problem is? We call repl* w/ the "compiler options" could't you just add the bit you need?

john 2024-11-24T21:10:45.423989Z

Add flag strings? like (conj (vec args) "-w" "src")?

john 2024-11-24T21:11:38.839299Z

Or use repl-opt?

john 2024-11-24T21:15:55.301149Z

Behaviors will be easier to sync up between all three repls by using repl* directly.

john 2024-11-24T21:17:19.977809Z

There's a good amount of logic in default-main and default-compile from the cli namespace I'm missing out on though

john 2024-11-24T21:27:46.419309Z

There's probably some way to do it with bindings

john 2024-11-24T21:34:11.517219Z

I vendored in the cljs.cli namespace at one point but it got complicated

souenzzo 2024-11-19T12:03:23.746819Z

is it required to use the name in your namespace? I don't think that it is a good idea to have 3-party libraries using cljs.repl.* namespace if this name is required, i think that the cljs main could be extended, allowing any namespace.

john 2024-11-19T12:41:24.965029Z

I thought it was required. Maybe it isn't? Do you know if it can already be done outside of cljs.repl.*?

john 2024-11-19T12:42:10.819769Z

Or would that extension point need to be added to clojurescript?

john 2024-11-19T12:46:17.336569Z

I mean, for sure, it could be built like clj -M -m my-repl-ws.main blah blah. I just like that you don't have to learn anything new (ideally) other than the new -re ws. The rest can just follow the normal clojure guides and docs. Not all features/flags pass through correctly right now but that's the goal.

john 2024-11-19T12:47:23.417069Z

Hmm, do you think I'm violating some copyright just by using cljs.* in a project 🤔 I hadn't thought of that

john 2024-11-19T12:55:03.895089Z

I view this as a "framework solution" - like where you can provide the system things in places where the system can pick them up. I wouldn't be opposed to clj -M -m cljs.main -re ... though, if y'all want to make it less frameworky

john 2024-11-19T12:56:23.623279Z

like, "if -re param has a dot in it, assume it's a foreign repl env and the whole namespace needs to be read out"

john 2024-11-19T13:01:26.946999Z

Not sure how that would work for the socket repl / prepl situation though, which I'm getting for free here too

john 2024-11-19T13:08:38.461019Z

I suppose the client side ns can be whatever I want, so I don't have to use the clojure/cljs names there

john 2024-11-19T13:09:43.543279Z

So it might be a little less jarring for users not expecting to come from a foreign lib

john 2024-11-19T13:34:31.336999Z

Oh, the socket repl / prepl takes the whole namespace, so it can already use foreign repl bits

john 2024-11-19T22:57:38.803919Z

It just occurred to me that I'd be squatting on a potential future namespace of the same name. I figured the different org coordinate would be sufficient to disambiguate within a given project. But when required by other libs, those libs might break (potentially?) if they want to move to org.clojure/cljs.repl.ws, if both libs don't have the same behavior. So I'll back out of that

john 2024-11-19T23:02:28.148999Z

Unless y'all are sure you never want to impl that namespace in the future and you don't mind if others sit on that namespace. Or I can put a PR together to allow namespaces for foreign repl env impls and then there won't be name clashes. Or just back out of it and only provide the socket repl/prepl. No big deal to me either way

dnolen 2024-11-20T14:28:42.329309Z

for starting up REPLs, that is how it works. It would probably be better to have your own specific some-ns.main like other REPLs do, then no conflicts.

dnolen 2024-11-20T14:32:00.454529Z

This is pretty minimal wrt. dependencies, but that's also why I dragged my feet on this mostly - adding a dependency even if it's only one library is really annoying.

john 2024-11-20T14:33:51.087589Z

Yeah I agree nobody is really asking for this. It's a thing I need every once in a while

john 2024-11-20T14:34:14.444809Z

Cool I'll do a main

john 2024-11-20T14:34:54.341269Z

Gonna call it https://en.m.wikipedia.org/wiki/Stoat :)

john 2024-11-20T14:42:04.151529Z

A stoat piggiebacking on a weasel piggiebacking on a woodpecker

dnolen 2024-11-20T14:45:31.551509Z

hehe