关于发送代码到 shadow-cljs 环境执行的细节

2

聊天记录长的我都记不住了, 写博客...

起因是看到了网上有人写 Clojure 然后用快捷键直接执行代码,
感觉还是蛮舒服的, 特别是用来教学的时候, 或者试验一些功能的时候,
因为直接在 REPL 里写肯定是很累的, 但是写在编辑器是文件, 不好单独执行,
而执行某一段选中的代码, 应该是不错的办法.
Lisp 跟 Smalltalk 都搞过...

首先, Clojure 是提供了一个功能的, nREPL, 可以通过网络发送代码到一个 REPL,
然后在这个 REPL 里执行代码, 得到执行的输出结果, 或者执行的报错.
现在已经知道的有两种实现, nREPL 和 Socket REPL, 有一些区别,
https://stackoverflow.com/que...
nREPL 是社区里做了比较久的一个工具, 而 Socket REPL 是官方临时加的一个小功能,
此外还有 unrepl 是一个新的东西, 还不熟悉. 以及一个不知道啥情况的 prepl.

由于我的代码是要在 shadow-cljs 连接的浏览器运行的, 所以整个都要围绕 shadow-cljs 搞.

nREPL

shadow-cljs 是支持的 nREPL 的:
https://shadow-cljs.github.io...

:nrepl {:port 9000 :middleware []}

按照文档配置好之后, nREPL 的 server 就启动了.
具体的内容是用 bencode 编码的, 感觉挺邪乎, 还是 BitTorrent 用的协议,
昨天在群里问的时候, 推荐我用 node 类库来搞这个事情:
https://github.com/rksm/node-...

捣腾了一下, 比如端口是 9000 的话, 可以这样发送代码过去:

n = require 'nrepl-client'
c = n.connect port: 9000

show = (err, result) -> console.error 'Error:', err; console.log 'Result', result

c.eval '(println "code")', show
c.eval '(+ 33 44)', show

然后代码就会在后台执行... 从 shadow-cljs 看不到 log, 但是从 API 能拿到返回的 output.

返回的 output 是挺让人崩溃的, 是一个 Vector, 包含几个 Map.
中间会带着一些 id, 以及返回的内容, 结构有点乱. 我手头没有留记录.

Socket REPL

Socket REPL 只是纯文本格式的发送 code, 返回执行结果,
shadow-cljs 的运行端口在 target/shadow-cljs/socket-repl.port 文件可以拿到,
或者通过配置文件去设置:

:socket-repl {:port 123}

同时有个私有的去掉 prompt 版本的 Socket REPL, 端口在 target/shadow-cljs/cli-repl.port.
说是内部使用的, 端口随机, 不建议使用, 那样我也尽量不去用吧...

连接 Socket REPL 用 TCP 协议直接就好, 参考 Node API 文档:
https://nodejs.org/api/net.ht...

net = require 'net'
c = net.createConnection port: 51053

c.on 'data', (data) -> console.log data.toString()

c.write '(println 1)\r\n'

返回的 output 就是一般的 REPL 运行的 log, 真的很一般, prompt 对开发工具很不友好.

unrepl

下午刚搜到过, Slack 上也只是刚认到人, 还不知道什么情况,
大致的印象是一个新的加 REPL 的方案, 基于 Lumo 做的, 启动很快,
但是还要再问一下具体能做什么...
https://github.com/Unrepl/unrepl
https://www.youtube.com/watch...

prepl

这个奇怪的东西还是 thheller 在 Slack 上说到的, 前几天的 commit 里出现的:
https://github.com/clojure/cl...
按照 Reddit 上的提示, 大致是发送代码, 返回结构化的结果的一个方案,

PREPL allows for (remoteable) streaming in, structured out.
This is useful for many kinds of REPL tooling allowing it to respond differently to eval output vs printing output.

https://www.reddit.com/r/Cloj...
跟 unrepl 相似, 结构化的数据更便于未来开发的工具链.
目前还不知道更多的细节, 等到 Clojure 1.10 估计会有一些解释.

通过 shadow-cljs 转发代码到前端

前面的代码运行的环境是 shadow-cljs 编译器自己的进程, 不是我的代码运行的环境,
而 shadow-cljs 当中, 使用 shadow-cljs cljs-repl 是可以发送代码到浏览器,
thheller 说如果要进一步切换到浏览器用的执行环境, 还需要再调用 API:

c.write '(shadow.cljs.devtools.api/repl :build-id)\n'

早几天的时候有个私有 API 可以用, 似乎不建议使用:

c.write '(shadow.cljs.devtools.cli/from-remote "some-uuid" "another-uuid" ["cljs-repl" "browser"])'

from-remote 在源码当中大概是这样, 反正我只能看个大概了...
https://github.com/thheller/s...

调用 API 之后再发送代码, 在浏览器就有反应了, 可以看到 log:

c.write '(println 4444)\n'

总之基于这个原理, 我应该是能在 Calcit Editor 当中实现我想要的功能了.

小结

后面还要在刷一下文档然后在 Calcit Editor 里加上对应的功能.
晚点再说了... 估计接下来会有一些新闻, 到时候再更新了.


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

载入中...