shadow-cljs

https://github.com/thheller/shadow-cljs | https://github.com/sponsors/thheller | https://www.patreon.com/thheller
13tales 2021-01-05T00:18:02.284200Z

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?

13tales 2021-01-05T00:33:01.286500Z

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

13tales 2021-01-05T00:43:25.288300Z

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.

thheller 2021-01-05T10:40:12.293100Z

just ctrl+c the watch and start it again

Alexis Vincent 2021-01-05T13:25:40.310700Z

Yes for clojure dep, no for npm dep

13tales 2021-01-05T22:59:53.341800Z

@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 😅

dpsutton 2021-01-05T01:56:49.288700Z

Just kill the process and restart the watch

👍 1
Gustavo Aguiar 2021-01-05T04:40:53.292100Z

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?

thheller 2021-01-05T10:37:13.292700Z

shadow-cljs is for compiling clojurescript and at most some .js files. it does not deal with any .java or JVM related things

thheller 2021-01-05T10:37:36.292900Z

I personally use lein for JVM related clj/java things

Gustavo Aguiar 2021-01-05T12:09:30.293700Z

Thanks for the explanation 😊

saitouena 2021-01-05T05:22:39.292200Z

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.

zendevil 2021-01-05T12:22:25.294400Z

I’m running a shadow watch

zendevil 2021-01-05T12:22:34.294700Z

but am getting the following error:

zendevil 2021-01-05T12:22:59.295Z

The required namespace “appname.core” is not available.

zendevil 2021-01-05T12:23:30.295600Z

even though the app name exists in the src/appname/core.cljs folder

zendevil 2021-01-05T12:23:36.295900Z

with the ns as appname.core

zendevil 2021-01-05T12:24:16.296300Z

here’s my shadow-cljs.edn file

zendevil 2021-01-05T12:24:17.296500Z

{: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]}}}}

thheller 2021-01-05T12:33:39.297200Z

@ps does appname maybe contain a -? Then the folder would need to be app_name

zendevil 2021-01-05T12:34:02.297500Z

it doesn’t contain a hyphen

zendevil 2021-01-05T12:36:45.298400Z

the folder and the namespace have the same name and contain only the alphabet characters

thheller 2021-01-05T12:37:46.298800Z

ah now I see it. it is :source-paths not :paths

zendevil 2021-01-05T12:40:23.299Z

thanks @thheller

zendevil 2021-01-05T12:48:50.299900Z

for some reason following these steps to compile to react native target isn’t working: https://morioh.com/p/ac368dc1fceb

zendevil 2021-01-05T12:50:55.300900Z

when I empty the index.js to just

import './app/index.js';
I get the error that it’s not registered

zendevil 2021-01-05T12:54:19.301400Z

actually I’m getting the error:

zendevil 2021-01-05T12:54:30.301700Z

element type is invalid: expected a string

zendevil 2021-01-05T12:54:36.301900Z

but got object

zendevil 2021-01-05T12:54:52.302200Z

you likely forgot to export the component from the file it is defined in

zendevil 2021-01-05T13:00:00.303400Z

is registerComponent required in ./index.js or does it come with the register-reload-comp?

thheller 2021-01-05T13:00:22.303700Z

sorry don't know what register-reload-comp is

thheller 2021-01-05T13:01:11.304300Z

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.

zendevil 2021-01-05T13:02:04.304500Z

that’s exactly what I do

zendevil 2021-01-05T13:02:16.304900Z

import ‘./app/index.js’;

zendevil 2021-01-05T13:02:39.305400Z

but that doesn’t register the app

thheller 2021-01-05T13:02:51.305700Z

element type is invalid: expected a string usually means that some component you are using was nil

thheller 2021-01-05T13:03:02.306Z

its your job to register the app

thheller 2021-01-05T13:03:11.306400Z

supposed to do it in the :init-fn

2021-01-05T13:03:29.307Z

Can you check ./app/index.js content and copy the first lines here?

zendevil 2021-01-05T13:03:30.307100Z

that’s what register-reload-comp does I think

thheller 2021-01-05T13:04:04.307500Z

yes but the error you are getting doesn't necessarily have anything to do with the root component

thheller 2021-01-05T13:04:20.307900Z

[:> something {:foo "bar"}] when something is nil you'll get this error

zendevil 2021-01-05T13:05:10.308Z

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;

zendevil 2021-01-05T13:06:15.308700Z

I’m not getting the expected string error when ./index.js just contains the import

thheller 2021-01-05T13:10:44.308900Z

do you use expo?

zendevil 2021-01-05T13:11:05.309300Z

no i don’t

2021-01-05T13:13:21.309800Z

What's your init defn (code snippet)?

zendevil 2021-01-05T13:15:00.310Z

(defn init [] (rn/register-reload-comp {:name “Something”} root-comp))

zendevil 2021-01-05T13:15:26.310300Z

also tried (defn init [] (AppRegistry {:name “Something”} root-comp))

zendevil 2021-01-05T13:15:40.310600Z

by doing [“react-native” :refer [AppRegistry SafeAreaView]]

zendevil 2021-01-05T13:27:40.311100Z

I meant, also tried this (defn init [] (. AppRegistry registerComponent {:name “Something”} root-comp))

zendevil 2021-01-05T13:28:41.311600Z

compiles successfully but Appname isn’t registered

thheller 2021-01-05T13:28:57.311900Z

this is CLJS {:name “Something”} maybe you want #js {:name “Something”}

zendevil 2021-01-05T13:29:34.312200Z

still registration problem 😞

2021-01-05T13:32:58.313100Z

Try like this:

(defn init []
  (rn/register-reload-comp "Something" root-comp))

zendevil 2021-01-05T13:33:48.313300Z

same problem

zendevil 2021-01-05T13:34:57.313500Z

not registered

zendevil 2021-01-05T13:38:36.314300Z

why does it say that registerComponent wasn’t called when it was called?

thheller 2021-01-05T13:39:19.314500Z

hard to say without all the code

zendevil 2021-01-05T13:41:10.314700Z

(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))

zendevil 2021-01-05T13:41:57.315100Z

and ./index.js is

import './app/index.js';

zendevil 2021-01-05T13:43:25.315900Z

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]}}}}

thheller 2021-01-05T13:47:45.316700Z

dont call register component like that

zendevil 2021-01-05T13:48:16.316900Z

how to call it instead?

thheller 2021-01-05T13:52:56.317100Z

like the example does

zendevil 2021-01-05T13:56:14.317600Z

you mean this repo? https://github.com/thheller/reagent-react-native

zendevil 2021-01-05T13:56:37.318400Z

I couldn’t search for registerComponent call in this repo

thheller 2021-01-05T13:57:14.318600Z

this call is doing that https://github.com/thheller/reagent-react-native/blob/master/src/main/test/app.cljs#L36

zendevil 2021-01-05T14:07:38.319400Z

I now have my code as the example but I’m still getting the not registered error:

zendevil 2021-01-05T14:07:40.319600Z

(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])))

thheller 2021-01-05T14:08:07.319900Z

is Root the name of your app? it needs to match whatever you have in app.json or so

zendevil 2021-01-05T14:08:36.320200Z

Okay now it works!

thheller 2021-01-05T14:08:54.321Z

I don't really do react-native myself so I'm just guessing with all of this 😛

zendevil 2021-01-05T14:08:56.321100Z

Thanks so much!

zendevil 2021-01-05T14:09:35.321200Z

Do you work with reagent web?

thheller 2021-01-05T14:12:57.321400Z

no. not using react at all anymore.

zendevil 2021-01-05T14:15:09.322900Z

what do you use instead>

thheller 2021-01-05T14:16:26.323400Z

https://github.com/thheller/shadow-experiments

thheller 2021-01-05T14:17:25.324100Z

not something anybody else should use yet but I like experimenting with new stuff 🙂

💡 1
flowthing 2021-01-05T14:18:50.324700Z

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
    (&lt;p! (.goto page "<https://playwright.dev>"))
    (is (= "Nope" (&lt;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?

Jakub Holý 2021-01-05T14:21:42.326500Z

Hello folks! What is the best practice for using JS libraries for the browser distributed via CDN? Simply add &lt;script ...&gt; to the html and use it via js/exportedThing ? Thank you!

thheller 2021-01-05T14:21:48.326700Z

core.async loses ^js typehint information unfortunately so its kinda hard to typehint that stuff properly

thheller 2021-01-05T14:22:37.327700Z

@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

thheller 2021-01-05T14:23:13.328Z

@holyjak yes thats fine. this also works https://shadow-cljs.github.io/docs/UsersGuide.html#js-resolve-global

❤️ 1
flowthing 2021-01-05T14:23:19.328200Z

@thheller Thanks! Will give that a try. Works great! 🙇:skin-tone-2:

mkvlr 2021-01-05T15:16:34.329600Z

has anyone tried to let closure handle modules generated by typescript?

Alexis Vincent 2021-01-05T15:28:38.329900Z

Yeah. Worked fine from my memory

mkvlr 2021-01-05T15:30:15.330200Z

@mail024 did you use https://github.com/angular/tsickle?

Alexis Vincent 2021-01-05T15:31:52.331300Z

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

Alexis Vincent 2021-01-05T15:32:45.332500Z

certainly wasnt an optimised experience. Was just for convinience pasting ts/js code into a file and using from cljs

mkvlr 2021-01-05T15:32:54.332700Z

ah oh, so using it like a normal node module

Alexis Vincent 2021-01-05T15:34:52.333500Z

exactly. I used the option on the ts compiler to keep directory structure. Then added the output to the classpath.

Alexis Vincent 2021-01-05T15:35:24.333900Z

So i had the ts files sitting alongside the cljs files.

Alexis Vincent 2021-01-05T15:36:06.334700Z

and a ./x.js in cljs land worked because the directory structure was persisted

thheller 2021-01-05T16:21:22.335200Z

if the code is ESM you will get full advanced optimizations when included via https://shadow-cljs.github.io/docs/UsersGuide.html#classpath-js

thheller 2021-01-05T16:22:17.336100Z

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

Alexis Vincent 2021-01-05T16:24:07.336800Z

@thheller You might find the conversation I just had with @dnolen on #clojurescript interesting.

thheller 2021-01-05T16:26:12.337400Z

I've been thinking about writing a cljs compiler that outputs only modern ESM code directly for a while now

thheller 2021-01-05T16:26:28.337800Z

but a whole lot of effort for little gain currently

lilactown 2021-01-05T20:10:00.338600Z

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

lilactown 2021-01-05T20:10:14.339Z

I had to hand-modify the files after compilation to use the old module system