This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-10-10
Channels
- # babashka (37)
- # babashka-sci-dev (22)
- # beginners (16)
- # biff (12)
- # calva (40)
- # cider (6)
- # clj-kondo (7)
- # clojure (183)
- # clojure-austin (20)
- # clojure-doc (22)
- # clojure-europe (16)
- # clojure-nl (2)
- # clojure-norway (39)
- # clojure-romania (1)
- # clojure-uk (9)
- # clojuredesign-podcast (9)
- # clojurescript (29)
- # core-typed (66)
- # cursive (19)
- # data-science (14)
- # docker (5)
- # fulcro (6)
- # hyperfiddle (46)
- # java (5)
- # malli (19)
- # missionary (3)
- # off-topic (84)
- # pedestal (5)
- # portal (36)
- # reitit (35)
- # releases (2)
- # shadow-cljs (30)
- # web-security (2)
- # yamlscript (1)
Request for guidance:
• I'm trying to develop a cljs plugin for a (closed-source) Electron app, Obsidian.md, and would like REPL support
• The app requires plugins to be bundled into a single JS file, which only happens with Shadow on release
as far as I can tell.
• However, Shadow only includes JS runtime repl code on dev
builds, which makes sense, but doesn't help in my case
One thought is to somehow explicitly include the node repl client code in the plugin (as would be done automatically for a dev build), build a release
, and run that build to connect to shadow server and provide the JS runtime.
1. Does this sound like a viable solution, or is there a potentially better approach?
2. I'm looking at shadow/cljs/devtools/client/node.cljs
, but it doesn't look designed for public consumption. Is there a clean way to start the node repl client in a non-dev build?
Thanks!
I was afraid of that. FWIW, I was able to start a sci-powered node repl from within the plugin (similar to how vs code Joyride does it), but this isn't a build-aware repl and doesn't work well with Cider. Here is the Typescript sample plugin repo: https://github.com/obsidianmd/obsidian-sample-plugin It includes an esbuild config that bundles everything into a single .js file.
{:deps true
:nrepl {:port 8702}
:compiler-options {:infer-externs :auto
:output-feature-set :es-next
:source-map true
:source-map-detail-level :all
:warnings {:fn-deprecated false}}
:builds
{:plugin {:target :node-library
;; :asset-path ""
:output-dir "cljs-out"
:output-to "saber.js"
:exports {:evalCljs saber.core/eval-cljs
:nreplStart saber.nrepl/start-server+
:nreplStop saber.nrepl/stop-server+
:loadFile saber.core/load-file
:main saber.core/main
;; :nrepl saber.nrepl-server/start
}
:compiler-options {:optimizations :none
:externs ["datascript/externs.js"]}
:modules {:saber {:entries [saber.core]}}
;; :build-hooks [(portal.shadow.remote/hook)]
:devtools {
;; :preloads [devtools.preload]
;; :after-load core/reload
:repl-pprint true
:watch-dir "cljs-out"}
:release {:compiler-options {:optimizations :simple}
:externs "datascript/externs.js"}}}}
I looked at this for a bit. there is no easy way to make this work. the entire thing is locked down a little bit too much.
the shadow-cljs node repl implementation requires access to the disk, but the plugin stuff isn't allowed to
do you explicitely need a REPL into the obsidian process, or do you just want a REPL?
Thanks for looking into it—I really appreciate it. The security model is a bit ... weird. SHADOW_IMPORT statements are not allowed (the plugin needs everything concatenated into a single file), but then you can freely use the "fs" module to access the file system. ¯\(ツ)/¯ What I have been able to do is: 1. Connect to a node REPL on an independent node process 2. Start a node nREPL from within the plugin using SCI for evaluation and connect to it using Calva (no Cider without piggyback) Both of these give me a partially functional repl, but neither is perfect. Honestly, it's the browser side of Electron that I need access to more than the Node side. Plugins are js modules, not HTML pages, but maybe if I inject some JS into the DOM on plugin initialization, I can get a functional browser repl...
Not sure if this is relevant but I did something similar writing a plugin for Joplin (an Obsidian alternative) which also uses Electron. I had to use build target :browser
to get it to work properly.
got curious how the plugin system works. turns out it isn't node. just the usual electron renderer process (i.e. a browser)
const { Plugin } = require("obsidian");
exports.default = class MyPlugin extends Plugin {
load() {
console.log("load plugin");
return import("");
}
unload() {
console.log("unload plugin");
}
};
seems to work just fine, but will require some more adjustments if you want access to the obsidian
(or other provided ones)
this is really the only important bit to know, then you can hack everything else.
function(e, t) {
return window.eval("(function anonymous(require,module,exports){".concat(e, "\n})\n//# sourceURL=").concat(t, "\n"))
}(i, "plugin:" + encodeURIComponent(e))(o, s, a),
of course you wouldn't use that setup for anything "production", but for development its fine
FYI, this works great. Thanks for your guidance!
I added :js-options {:keep-native-requires true}
to support node native modules.
I did run into what I think is a bug when trying to connect to the REPL: The browser (electron) side of the connection attempts to connect to
even though I have specified :http {:host "localhost"}
.
For now, I have just manually overridden this, but can dig into why this is happening assuming I'm not just missing some necessary configuration.