Hey there. I’m a relative newbie just getting to grips with Clojurescript and shadow-cljs, and have a quick workflow/REPL question:
I’m running/hot-reloading my application with shadow-cljs watch
, and connecting to the REPL with Cider.
When I add a new dependency to shadow-cljs.edn
, do I need to stop and restart the watch
command in order to be able to use it, or is there something I can do via the REPL to install it and make it available right away?
Okay, found the answer to my own question: it looks like adding/changing dependencies does always require restarting the server, if I’m understanding the docs correctly. > Once the server is running however you only have to restart it whenever your :dependencies change and everything else can be done via the REPL. https://shadow-cljs.github.io/docs/UsersGuide.html#_server_mode
Would still appreciate some clarity on this, actually:
If I’m developing with shadow-cljs watch
, what should I do when I add a dependency?
Trying to restart the server by running shadow-cljs restart
in another terminal seemed to break the connection with the browser and REPL.
just ctrl+c the watch and start it again
Yes for clojure dep, no for npm dep
@thheller Thanks. Still new to the ecosystem, and wanted to make sure there wasn’t some galaxy-brain clojure-wizard I-never-leave-my-REPL way of updating the dependencies that I was missing 😅
Just kill the process and restart the watch
Hi all, I'm learning Clojure(Script) and for that I'm doing a little project. In this little project I setup a JNI with Rust, which made me use lein
so that I can correctly compile the Java file I had in my project.
I'm also the kind of person who likes to write to learn, and I'm writing about the project setup. But to truly write something good I have to understand why I had to use lein
(I honestly don't know, a friend simply said it would work if I did, and it worked). Is there any resource where I can understand if shadow-cljs can compile .java files, and if not, why it can't?
shadow-cljs is for compiling clojurescript and at most some .js files. it does not deal with any .java or JVM related things
I personally use lein for JVM related clj/java things
Thanks for the explanation 😊
As far as I know, shadow-cljs cannot compile .java files because compiling .java files is not the purpose of shadow-cljs. I think reading the official documentation would help you. lein: https://github.com/technomancy/leiningen > Leiningen is for automating Clojure projects without setting your hair on fire. lein is a kind of build tool for clojure(script). shadow-cljs: https://shadow-cljs.github.io/docs/UsersGuide.html#_introduction the purpose of shadow-cljs is compiling cljs code into javascrpt codes with ease.
I’m running a shadow watch
but am getting the following error:
The required namespace “appname.core” is not available.
even though the app name exists in the src/appname/core.cljs folder
with the ns as appname.core
here’s my shadow-cljs.edn file
{:paths [“src”] :dependencies [[reagent “0.10.0"] [re-frame “0.12.0”] [re-frame-steroid “0.1.1"] [rn-shadow-steroid “0.2.1”] [re-frisk-remote “1.3.3"]] :builds {:dev {:target :react-native :init-fn appname.core/init :output-dir “app” :compiler-options {:closure-defines {“re_frame.trace.trace_enabled_QMARK_” true}} :devtools {:after-load steroid.rn.core/reload :build-notify steroid.rn.core/build-notify :preloads [re-frisk-remote.preload]}}}}
@ps does appname
maybe contain a -
? Then the folder would need to be app_name
it doesn’t contain a hyphen
the folder and the namespace have the same name and contain only the alphabet characters
ah now I see it. it is :source-paths
not :paths
thanks @thheller
for some reason following these steps to compile to react native target isn’t working: https://morioh.com/p/ac368dc1fceb
when I empty the index.js to just
import './app/index.js';
I get the error that it’s not registeredactually I’m getting the error:
element type is invalid: expected a string
but got object
you likely forgot to export the component from the file it is defined in
is registerComponent required in ./index.js or does it come with the register-reload-comp?
sorry don't know what register-reload-comp
is
the index.js is just supposed to load the CLJS output, it shouldn't do anything else. at least in the examples I set up.
that’s exactly what I do
import ‘./app/index.js’;
but that doesn’t register the app
element type is invalid: expected a string
usually means that some component you are using was nil
its your job to register the app
supposed to do it in the :init-fn
Can you check ./app/index.js
content and copy the first lines here?
that’s what register-reload-comp does I think
yes but the error you are getting doesn't necessarily have anything to do with the root component
[:> something {:foo "bar"}]
when something
is nil you'll get this error
var $CLJS = global; var shadow$start = new Date().getTime(); var shadow$provide = global.shadow$provide = {}; var goog = global.goog = {}; global.CLOSURE_DEFINES = {“shadow.cljs.devtools.client.env.repl_pprint”:false,“shadow.cljs.devtools.client.env.reload_strategy”:“optimized”,“shadow.cljs.devtools.client.env.devtools_url”:“”,“shadow.cljs.devtools.client.env.autoload”:true,“shadow.cljs.devtools.client.env.proc_id”:“cd2d6a99-7fe1-4f1e-bcf2-1a6e460663d8",“shadow.cljs.devtools.client.env.use_document_protocol”:false,“goog.ENABLE_DEBUG_LOADER”:false,“shadow.cljs.devtools.client.env.server_port”:9630,“shadow.cljs.devtools.client.env.server_token”:“1ef12036-eb29-40ca-ab13-c6eb506840ad”,“shadow.cljs.devtools.client.env.use_document_host”:true,“shadow.cljs.devtools.client.env.module_format”:“goog”,“goog.LOCALE”:“en”,“re_frame.trace.trace_enabled_QMARK_“:true,“shadow.cljs.devtools.client.env.build_id”:“dev”,“shadow.cljs.devtools.client.env.ignore_warnings”:false,“goog.DEBUG”:true,“cljs.core.STARtarget_STAR_“:”react-native”,“shadow.cljs.devtools.client.env.log”:true,“shadow.cljs.devtools.client.env.ssl”:false,“shadow.cljs.devtools.client.env.custom_notify_fn”:“steroid.rn.core.build_notify”,“shadow.cljs.devtools.client.env.enabled”:true,“shadow.cljs.devtools.client.env.server_host”:“192.168.1.40”,“shadow.cljs.devtools.client.env.worker_client_id”:2,“goog.TRANSPILE”:“never”}; var COMPILED = false; var goog = goog || {}; goog.global = global; goog.global.CLOSURE_UNCOMPILED_DEFINES; goog.global.CLOSURE_DEFINES; goog.isDef = function(val) { return val !== void 0; }; goog.isString = function(val) { return typeof val == “string”; }; goog.isBoolean = function(val) { return typeof val == “boolean”; }; goog.isNumber = function(val) { return typeof val == “number”; }; goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { var parts = name.split(“.”); var cur = opt_objectToExportTo || goog.global; if (!(parts[0] in cur) && typeof cur.execScript != “undefined”) { cur.execScript(“var ” + parts[0]); } for (var part; parts.length && (part = parts.shift());) { if (!parts.length && opt_object !== undefined) { cur[part] = opt_object; } else { if (cur[part] && cur[part] !== Object.prototype[part]) { cur = cur[part]; } else { cur = cur[part] = {}; } } } }; goog.define = function(name, defaultValue) { var value = defaultValue; if (!COMPILED) { var uncompiledDefines = goog.global.CLOSURE_UNCOMPILED_DEFINES; var defines = goog.global.CLOSURE_DEFINES; if (uncompiledDefines && uncompiledDefines.nodeType === undefined && Object.prototype.hasOwnProperty.call(uncompiledDefines, name)) { value = uncompiledDefines[name]; } else { if (defines && defines.nodeType === undefined && Object.prototype.hasOwnProperty.call(defines, name)) { value = defines[name]; } } } return value; }; goog.FEATURESET_YEAR = goog.define(“goog.FEATURESET_YEAR”, 2012); goog.DEBUG = goog.define(“goog.DEBUG”, true); goog.LOCALE = goog.define(“goog.LOCALE”, “en”); goog.TRUSTED_SITE = goog.define(“goog.TRUSTED_SITE”, true); goog.STRICT_MODE_COMPATIBLE = goog.define(“goog.STRICT_MODE_COMPATIBLE”, false); goog.DISALLOW_TEST_ONLY_CODE = goog.define(“goog.DISALLOW_TEST_ONLY_CODE”, COMPILED && !goog.DEBUG); goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING = goog.define(“goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING”, false); goog.provide = function(name) { if (goog.isInModuleLoader_()) { throw new Error(“goog.provide cannot be used within a module.“); } if (!COMPILED) { if (goog.isProvided_(name)) { throw new Error(‘Namespace “’ + name + ‘” already declared.’); } } goog.constructNamespace_(name); }; goog.constructNamespace_ = function(name, opt_obj) { if (!COMPILED) { delete goog.implicitNamespaces_[name]; var namespace = name;
I’m not getting the expected string error when ./index.js just contains the import
do you use expo?
no i don’t
What's your init defn (code snippet)?
(defn init [] (rn/register-reload-comp {:name “Something”} root-comp))
also tried (defn init [] (AppRegistry {:name “Something”} root-comp))
by doing [“react-native” :refer [AppRegistry SafeAreaView]]
I meant, also tried this (defn init [] (. AppRegistry registerComponent {:name “Something”} root-comp))
compiles successfully but Appname isn’t registered
this is CLJS {:name “Something”}
maybe you want #js {:name “Something”}
still registration problem 😞
Try like this:
(defn init []
(rn/register-reload-comp "Something" root-comp))
same problem
not registered
why does it say that registerComponent wasn’t called when it was called?
hard to say without all the code
(ns appname.core
(:require [steroid.rn.core :as rn]
["react-native" :refer (View AppRegistry SafeAreaView)]))
(defn root-comp []
[:> View "view"]
#_[rn/view
[rn/text "Hello CLojure! from CLJS"]])
(defn init []
(. AppRegistry registerComponent #js {:name "Something"} root-comp))
and ./index.js is
import './app/index.js';
shadow-cljs.edn is
{:source-paths ["src"]
:dependencies [[reagent "0.10.0"]
[re-frame "0.12.0"]
[re-frame-steroid "0.1.1"]
[rn-shadow-steroid "0.2.1"]
[re-frisk-remote "1.3.3"]]
:builds {:dev
{:target :react-native
:init-fn appname.core/init
:output-dir "app"
:compiler-options {:closure-defines
{"re_frame.trace.trace_enabled_qmark_" true}}
:devtools {:after-load steroid.rn.core/reload
:build-notify steroid.rn.core/build-notify
:preloads [re-frisk-remote.preload]}}}}
dont call register component like that
how to call it instead?
like the example does
you mean this repo? https://github.com/thheller/reagent-react-native
I couldn’t search for registerComponent call in this repo
this call is doing that https://github.com/thheller/reagent-react-native/blob/master/src/main/test/app.cljs#L36
I now have my code as the example but I’m still getting the not registered error:
(ns appname.core (:require [steroid.rn.core :as rn] [shadow.react-native :refer (render-root)] [reagent.core :as r] [“react-native” :refer (Text SafeAreaView View AppRegistry SafeAreaView)])) (defn root-comp [] [:> SafeAreaView [:> View [:> Text “Some Text”]]]) (defn init [] (render-root “Root” (r/as-element [root-comp])))
is Root
the name of your app? it needs to match whatever you have in app.json or so
Okay now it works!
I don't really do react-native myself so I'm just guessing with all of this 😛
Thanks so much!
Do you work with reagent web?
no. not using react at all anymore.
what do you use instead>
not something anybody else should use yet but I like experimenting with new stuff 🙂
Don't know if this more of a ClojureScript or a shadow-cljs issue, but I guess I'll try my luck here first: I'm working on integrating Playwright (https://playwright.dev) into one of my projects. I have a macro like this:
(defmacro test-page
[browser-types page-sym & body]
`(cljs.test/async done#
(cljs.core.async/go
(doseq [browser-type# ~browser-types]
(let [browser# (cljs.core.async.interop/<p! (.launch browser-type#))
page# (cljs.core.async.interop/<p! (.newPage browser#))
~page-sym page#]
~@body
(done#))))))
And I call it like this:
(deftest smoke
(test-page [chromium] page
(<p! (.goto page "<https://playwright.dev>"))
(is (= "Nope" (<p! (.innerText page ".navbar__title"))))))
It works well, but I get a lot of inference warnings like this:
10 | (test-page [chromium] page
---------^----------------------------------------------------------------------
Cannot infer target type in expression (. inst_47307 (newPage))
I'm not sure how to fix those warnings. Any ideas?Hello folks! What is the best practice for using JS libraries for the browser distributed via CDN? Simply add <script ...>
to the html and use it via js/exportedThing
? Thank you!
core.async
loses ^js
typehint information unfortunately so its kinda hard to typehint that stuff properly
@flowthing you can turn off the warnings via :compiler-options {:infer-externs true}
(not :auto
). that way you only get warnings if you (set! *warn-on-infer* true)
in the ns
@holyjak yes thats fine. this also works https://shadow-cljs.github.io/docs/UsersGuide.html#js-resolve-global
@thheller Thanks! Will give that a try. Works great! 🙇:skin-tone-2:
has anyone tried to let closure handle modules generated by typescript?
Yeah. Worked fine from my memory
@mail024 did you use https://github.com/angular/tsickle?
No, no experience with that. but thats probably the way to go to get optimisations. I just compiled to js and used shadow’s js integration
certainly wasnt an optimised experience. Was just for convinience pasting ts/js code into a file and using from cljs
ah oh, so using it like a normal node module
exactly. I used the option on the ts compiler to keep directory structure. Then added the output to the classpath.
So i had the ts files sitting alongside the cljs files.
and a ./x.js in cljs land worked because the directory structure was persisted
if the code is ESM you will get full advanced optimizations when included via https://shadow-cljs.github.io/docs/UsersGuide.html#classpath-js
only trouble is externs inference doesn't work for the JS code so if that code uses a lot of npm code you might need manual externs
I've been thinking about writing a cljs compiler that outputs only modern ESM code directly for a while now
but a whole lot of effort for little gain currently
I went down the route of tsickle for a lib awhile back but IIRC the goog.module
output it produces wasn't amenable for use with CLJS
I had to hand-modify the files after compilation to use the old module system