-
Notifications
You must be signed in to change notification settings - Fork 85
Conversation
@anmonteiro and I had a conversation on slack where we decided on the following changes to mimic Clojure's socket server behavior. Here's the Clojure Socket Server REPL design doc, and currently the following is going to get done.
Some things I don't believe we can test with jest:
So, whatever we want to test, it has to A) be exported and B) can't involve testing non-exported variables/functions. I'm not familiar enough with the JS testing landscape to offer suggestions, but I'll endeavor to test what I can within the constraints of the framework. |
695ab5f
to
ad4f6e0
Compare
ad4f6e0
to
5690b99
Compare
8c799c6
to
5dbf0fb
Compare
11797fc
to
85c5434
Compare
src/cljs/snapshot/lumo/repl.cljs
Outdated
@@ -1370,6 +1364,7 @@ | |||
(merge opts {:ns (symbol ns-sym)}) | |||
(fn [{:keys [ns value error] :as ret}] | |||
(try | |||
(apply value socket fn-args) | |||
;; TODO: do we wanna splice args? | |||
(value socket fn-args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I'm thinking about it, we should probably switch value
to fn-symbol
or something that's representative of what it holds.
The current implementation of the socket server is built specifically around running a repl, instead of allowing other possible functions to handle input from the socket. I've mostly pulled out the repl specific code into it's own function, and allowed for passing an `accept` function (following the same naming convention as clojure.core.server) to the `open` function in socketRepl.js. There's still a call to unhookOutputStreams inside of the `close` function that I believe isn't necessary since this returns the stdout/stdin for the process to the original value, but we're calling it as the process is being shut down, so that's cleaned up anyways. More importantly, any cleanup for a particular accept function should happen inside that accept function, as we won't be able to generalize the socket server otherwise.
I wanted to clarify some design decisions so I cleaned up the comments a bit.
We're not using weak types like Function or any, so I've created an AcceptFn type for the accept functions.
Since we can't use weak return types I believe I need to decide on a return type for every accept function. Since currently we don't use the return value of the accept function, and I can't currently think of a reason for an accept function to have a return value, I've set the default return type to void. I've tested these changes locally against eslint and flow, so hopefully I've covered all of the bases this time.
I've actually fixed the broken tests and walked through the steps the CI will do, so this commit should run properly.
If we want to resolve ClojureScript functions we'll need to have access to the ClojureScriptContext, which means the Socket REPL initialize has to happen after the ClojureScript engine is started. Since the startClojureScriptEngine starts the engine as well as running whatever scripts, main functions, etc. that the user has started, it made sense to put the Socket REPL code there as well.
This means we can now define an arbitrary cljs function as the accept function. I've mimicked the work in run-main for resolving the namespace the function is in, and then running it.
You can now specify a namespaced CLJS function which will be called when a client connects. It can currently accept a single argument, which is a socket.
We can now process JSON args, host:port, and port specifications with the -n flag.
The repl error handler changed when I rebased, so now it's up to date.
For some reason I need to init the cljs engine twice for the repl to start. TODO: figure out why.
The parsing is done, and now I'm working on getting the args to parse properly. When I `JSON.parse` the args in JS then pass them to CLJS via the `ClojureScriptContext.lumo.repl.run_accept_fn.call` in cljs.js, it turns into some form of object that `js->clj` can't parse into a cljs map (at least, println doesn't print it properly, but console.log does). If I run `(js->clj (js/JSON.parse (js/JSON.stringify (first args))))` in lumo/repl.cljs then everything comes out fine. I'm not sure why this is failing...
When building the dev scripts, for some reason passing a JSON object from the JS code to the CLJS code means we can't convert to cljs objects with `js->cljs`. This apparently worked if you build the release executable, but that leaves dev hard to develop/test/debug. So I've introduced a workaround.
Previously I was just checking for the instantiation of replOpts, as a determiner for whether we should pull it apart to get the host/port/etc. Now this works as expected.
If you pass bad data to the Socket REPL it should throw a SyntaxError.
We're using promises to ensure errors in the socket repl don't blow up the whole repl, so to ensure the server is started before printing it's details we're using async/await. Not that I think about it, this should probably go into the socketREPL namespace, which would change the far reaching nature of these changes. Hmmm...
14c55c7
to
7709e3a
Compare
Here's the initial work to allow the socket server to accept a namespaced function via the CLI and run it when someone connects.
I've got a test namespace in
tst/hello/world.cljs
that just prints\nHello friend.\n\n
and closes the socket.You can call it with
-A 'namespaced.fn/here'