shadow-cljs

https://github.com/thheller/shadow-cljs | https://github.com/sponsors/thheller | https://www.patreon.com/thheller
2021-04-13T04:52:54.107900Z

is there a way to apply preloads only to some modules and not all. I am trying to create a web-worker, but the preloads assume the main thread js environment is present and are resulting in an exception when the worker starts.

thheller 2021-04-13T06:49:48.108Z

I do recommend using your own server if you do this. :dev-http is meant as a STATIC webserver and using a custom handler is not recommended

thheller 2021-04-13T06:50:07.108200Z

that being said it does support websockets just fine, just probably not the ring variant or sente

thheller 2021-04-13T06:50:46.109Z

@danvingo you can set :preloads per module yes. just {:modules {:main {:init-fn <http://my.app/init|my.app/init> :preloads [foo.bar]}}}

2021-04-13T12:19:57.113700Z

Thanks! That worked, but these are dev-time only preloads, is there a way to include them only during dev and not a release? I tried nesting them under :dev but they aren't being included now:

{:modules {:main { :dev 
;; moving to here:
{:preloads [...]} }
          :worker{}}
:devtools          {:after-load         app.client.client-entry/refresh
                                          :http-port          8044
                                          :http-resource-root "public"
                                          :http-root          "resources/public"
;; moving from here
                                          :preloads           [com.fulcrologic.fulcro.inspect.preload
                                                               app.client.development-preload
                                                               com.fulcrologic.fulcro.inspect.dom-picker-preload]}}

thheller 2021-04-13T13:14:53.115100Z

:preloads are ALWAYS developmen only and do not apply to release builds

thheller 2021-04-13T13:15:00.115300Z

so just put it into modules like I said

2021-04-13T13:41:51.126Z

perfect, thank you!

grounded_sage 2021-04-13T12:14:39.113600Z

I have been digging through old Clojurians messages and issues on Github related to connecting a repl to different runtime environments we need to deal with when doing Chrome extensions. Still unclear to me if this is possible... let alone how to do it. It seemed that @wilkerlucio mentioned that he had it working on background and devtool but not content-script. Though I have not been able to get any working. Wondering what the current state of this is and whether it is worth investing any more time in trying to get it working. Seems that there is a lot of nrepl and sourcemap related issues..

2021-04-13T13:03:40.114200Z

Using shadowcljs$aot version, constantly seeing this error:

[:mobile] Build completed. (203 files, 91 compiled, 0 warnings, 13.29s)
Exception in thread "async-dispatch-2" java.lang.IllegalArgumentException: No implementation of method: :close! of protocol: #'clojure.core.async.impl.protocols/Channel found for class: nil
        at clojure.core$_cache_protocol_fn.invokeStatic(core_deftype.clj:583)
        at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:575)
        at clojure.core.async.impl.protocols$eval1077$fn__1078$G__1066__1083.invoke(protocols.clj:21)
        at clojure.core.async.impl.ioc_macros$run_state_machine_wrapped.invokeStatic(ioc_macros.clj:979)
        at clojure.core.async.impl.ioc_macros$run_state_machine_wrapped.invoke(ioc_macros.clj:975)
        at clojure.core.async$ioc_alts_BANG_$fn__3907.invoke(async.clj:384)
        at clojure.core.async$do_alts$fn__3839$fn__3842.invoke(async.clj:253)
        at clojure.core.async.impl.channels.ManyToManyChannel$fn__1491.invoke(channels.clj:265)
        at clojure.lang.AFn.run(AFn.java:22)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
        at java.base/java.lang.Thread.run(Thread.java:832)

thheller 2021-04-13T13:16:45.115800Z

@i that means you are also using a conflicting too old core.async version as a dependency

thheller 2021-04-13T13:17:10.116400Z

with all AOT code you basically need to have the version that the AOT code used, otherwise you'll get errors such as that

2021-04-14T12:09:47.182300Z

I wonder why that makes a difference. With or without aot, I thought using an old library will both fail, not aot alone.

thheller 2021-04-14T12:16:12.182500Z

just an artifact of how clojure AOT code works, doesn't mean the non AOT code will work. it might fail at a different point in the code and not directly on load

thheller 2021-04-14T12:16:39.182700Z

might also just work but either way you should ensure to use a compatible version

thheller 2021-04-13T13:19:26.118Z

@grounded_sage I cannot help without seeing your config or what you are actually doing. chrome extensions are rather difficult since they do have all sorts of restrictions but the repl and hot-reload do work. I cannot tell you more since I don't know what you have tried. this is still pretty much the only "documentation" that exists https://github.com/thheller/shadow-cljs/issues/279

grounded_sage 2021-04-13T13:23:58.120100Z

Here is the shadow-cljs.edn and the manifest.edn @thheller. Currently I get a tonne of few websocket errors that are thrown in the console and a bunch of sourcemap errors. https://github.com/homebaseio/datalog-console/blob/extension/shadow-cljs.edn https://github.com/homebaseio/datalog-console/blob/extension/shells/chrome/manifest.edn

grounded_sage 2021-04-13T13:24:53.121400Z

Have mostly been aligning with the file structure and set up done for fulcro-inspect so that it is easier to reference across the two and see differences.

thheller 2021-04-13T13:25:00.121600Z

then you are likely just blocking yourself with your CSP rules?

thheller 2021-04-13T13:26:25.123100Z

I can never remember the exact rules for CSP but it needs to be able to connect to localhost:9630 (the shadow-cljs instance server)

grounded_sage 2021-04-13T13:26:32.123200Z

ah sorry on my local that is set to 9630. Must have been a recent change I did not push.

thheller 2021-04-13T13:27:42.123900Z

I mean if you get warnings/errors already then they are the hint to fix it as well

thheller 2021-04-13T13:28:02.124200Z

you can paste some and I can maybe tell you what they mean

grounded_sage 2021-04-13T13:42:30.126500Z

Okay. Seems that changing the CSP url to be the same as devtools-url removes the ws errors that are thrown. But this makes it different to what is happening in fulcro-inspect. Which has 9632 in the devtools-url and then 9630 in the CSP rules.

grounded_sage 2021-04-13T13:43:41.127600Z

I get this when I open up the chrome console inside of the chrome-panel. Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.

grounded_sage 2021-04-13T13:45:05.129100Z

In both background (when clicking the url of extension inspect views) and the chrome panel I get a lot of source map loading errors DevTools failed to load source map: Could not load content for <chrome-extension://liafbncjkfdikgnjaoilhpgjbgcmnfcb/out/cljs-runtime/goog.debug.error.js.map>: HTTP error: status code 404, net::ERR_UNKNOWN_URL_SCHEME

thheller 2021-04-13T13:45:05.129200Z

well is it actually running on 9630 or 9632? it is logged on startup

thheller 2021-04-13T13:48:56.131500Z

well if you have multiple shadow-cljs instances running locally and 9630 is taken it'll use 9631 then 9632 and so on

thheller 2021-04-13T13:49:13.131900Z

don't use :devtools-url at all if you want to avoid that headache

thheller 2021-04-13T13:49:25.132200Z

ah wait no you have to

thheller 2021-04-13T13:49:41.132400Z

but yeah use your port ...

thheller 2021-04-13T13:56:54.133400Z

for source maps try :source-map-inline true in :compiler-options

grounded_sage 2021-04-13T14:24:45.136500Z

:source-map-inline true worked for chrome-extension target but doesn't seem to be working for chrome target https://github.com/homebaseio/datalog-console/blob/extension/shadow-cljs.edn#L21. Though perhaps I have something else configured wrong here? I also seem to be able to connect to the background runtime with the repl when I set my devtools-url to 9630. When the devtools-url is set to 9632 I get WebSocket connection to '<ws://localhost:9632/api/remote-relay?server-token=7065c100-73ba-4059-b4cc-774efebf28fe>' failed: followed by shadow-cljs - remote-error several times before it gives up trying to connect.

thheller 2021-04-13T14:25:57.137600Z

I do not understand why you are trying to connect to 9632. shadow-cljs ONLY starts ONE port. there is no such extra magic port with regards to chrome-extension

thheller 2021-04-13T14:26:25.138400Z

so stick with 9630 if that is the port you are starting shadow-cljs with

grounded_sage 2021-04-13T14:26:26.138500Z

I was just following the configuration from fulcro-inspect

thheller 2021-04-13T14:26:45.139Z

but it clearly doesn't apply to your setup. it can only connect to a port that actually exists, so don't configure one that doesn't

thheller 2021-04-13T14:27:37.139500Z

as for the REPL connection. open http://localhost:9630/runtimes

thheller 2021-04-13T14:27:49.140Z

that will list all the connected runtimes with their respective ids

👍 1
thheller 2021-04-13T14:28:04.140600Z

you can switch to a specific one but the default rule is to connect to the first one that connects

grounded_sage 2021-04-13T14:28:23.140800Z

okay. We are doing similar things. Plan to also build an electron app so most of what was done there I thought would apply.

grounded_sage 2021-04-13T15:29:27.143400Z

Thanks for pointing out the url for open runtimes. Was a big help. I am able to connect to them with the repl now. Only thing left is source-maps not loading in the panel. But I think this is a path related issue on my end.

thheller 2021-04-13T15:45:09.144100Z

inline should fix it but might be a bug in the :chrome-extension target not supporting it properly or something

thheller 2021-04-13T15:45:15.144300Z

don't know, can't check right now

lispers-anonymous 2021-04-13T16:17:50.149500Z

I have a question about an issue I've run into with the :output-feature-set :es5 setting with shadow-cljs. When my compiled javascript is loaded I get an error that says

Uncaught TypeError: $jscomp.arrayFromIterable is not a function
    memoizedFn memoize.js:60
    setEnabled history.js:503
    &lt;anonymous&gt; events.cljs:458
    re_frame$fx$do_fx_after fx.cljc:59
    ... blah blah ...
    &lt;anonymous&gt; babel-polyfill.min.js:1
    m babel-polyfill.min.js:1
    b babel-polyfill.min.js:1
Using the browser console, I can see that the $jscomp object is defined, but the function arrayFromIterable is not defined on it. Other functions are. I was able to fix this by defining the function in a :prepend statement for my module.
:prepend "$jscomp.arrayFromIterable = function(a) {return a instanceof Array ? a : $jscomp.arrayFromIterator($jscomp.makeIterator(a));};"
It fixed my problem. My javascript loads but it feels dirty. Is there another way to solve this?

thheller 2021-04-13T16:18:47.149900Z

there is some issue with $jscomp I havent been able to track down yet

thheller 2021-04-13T16:19:07.150500Z

normally it just goes away after wiping the cache and recompiling. just try deleting .shadow/builds/&lt;build-id&gt;

lispers-anonymous 2021-04-13T16:19:18.150900Z

I attempted that, but it did not fix my issue

thheller 2021-04-13T16:19:20.151100Z

and then restarting the watch or release

thheller 2021-04-13T16:19:26.151300Z

hmm odd

lispers-anonymous 2021-04-13T16:19:49.152Z

It goes away when I target :es6 but I can't do that because of internet explorer 😞

thheller 2021-04-13T16:19:50.152100Z

I mean if you can make a reproducible minimal repro that would help a lot

thheller 2021-04-13T16:20:03.152300Z

IE is dead 😉

lispers-anonymous 2021-04-13T16:20:23.152700Z

Not for my enterprise customers unfortunately lol

lispers-anonymous 2021-04-13T16:22:21.154600Z

I will see if I can make a reproduceable small build. That might take me a while to do. I'm working on migrating a really large cljs application to shadow so there are a lot of moving parts. In the mean time, maybe that little hack will help someone.

emier 2021-04-13T16:51:56.158500Z

In testing, what’s a good way to run some js code from a file (`myPreloads.js` ), which mocks some things before running the main test function? Basically, achieving the same thing as copy-pasting the code from myPreloads.js to the beginning of the test/target/testable.js file (which obviously isn’t a good idea).

emier 2021-04-19T14:22:20.320900Z

Thanks for the reply @dannyfreeman, that seems to do what I want it to! However, now I get a problem with the modules that are required from the preloaded js file. So right now I have my preload namespace:

(ns preloads
  (:require ["./myPreloads.js"]))
and the js file I want to run, which requires its own modules myPreloads.js
...
require("module-alias/register");
...
I get the following error when trying to run the tests:
SHADOW import error /app/.shadow-cljs/builds/test/dev/out/cljs-runtime/myPreloads.js

/app/test/target/testable.js:67
    throw e;
    ^
Module not provided: shadow.js.shim.module$module_alias$register
Looking at the generated testfile testable.js, this is where it does the import.
SHADOW_IMPORT("shadow.js.shim.module$module_alias$register.js");
Can’t really figure out how to require these modules so that shadow can find them. Am I missing something here?

lispers-anonymous 2021-04-19T14:24:55.321200Z

That I am unsure about. I did run into some issues around testing code meant for a browser for a node-test target that seems similar. To fix those issues I had to set this option in my :test profile in shadow-cljs.edn file

:js-options {:entry-keys ["module" "browser" "main"]}

lispers-anonymous 2021-04-19T14:25:48.321400Z

I'm really unsure what the implications of doing that are, but combining that with stubbing out the global window object got my tests to run in node.

lispers-anonymous 2021-04-19T14:45:43.321700Z

Also, if the contents of myPreloads.js are pretty simple, you could try re-writing them in clojurescript directly in your preloads cljs file. Perhaps shadow-cljs doesn't understand the code in your js file.

emier 2021-04-19T14:48:52.321900Z

Okay, from the docs it seems like :js-options determines which build variant to use for the npm package, e.g. CommonJS or ESM. Unfortunately, adding it doesn’t do anything for me. Unfortunately, myPreloads.js is quite extensive so I’m trying to avoid rewriting it in cljs.

emier 2021-04-19T14:53:46.322800Z

@thheller any ideas on how to fix this error related to shadow-cljs being unable to import npm modules that are required from a local js-file which in turn was required in a preload namespace? More info in my first reply of this thread

emier 2021-04-19T14:55:30.323Z

Not sure if it makes a difference, but the modules it can’t find seems to be devDependencies

thheller 2021-04-19T15:02:13.323200Z

how are you running the tests? it is :node-test I presume?

emier 2021-04-20T06:40:16.342400Z

yes, using :node-test

thheller 2021-04-20T06:45:09.342600Z

if your preloads only setup node related things and don't interact with CLJS in any way you can just use node -r ./preloads.js your-test-output.js

thheller 2021-04-20T06:45:35.342800Z

there are ways to get it into the build of course but this would be the fastest and easiest

emier 2021-04-20T06:54:17.343Z

okay, it seems it could work, is there a way to make :autorun run this as well though?

thheller 2021-04-20T07:12:29.343300Z

no there is not but I don't recommend using that anyways

thheller 2021-04-20T07:13:23.343500Z

you are much better off using something like https://www.npmjs.com/package/chokidar-cli to watch the test output file and have that rerun the tests when needed

thheller 2021-04-20T07:13:42.343900Z

that also gives you full control over how tests run and so on

emier 2021-04-20T07:16:40.344100Z

Thanks for the tip, I’ll look into it! Just curious, what’s the reason behind the nested requires not working when using preloads?

thheller 2021-04-20T07:20:43.344300Z

it should work but what the code does actually matters and I don't have enough information to say

thheller 2021-04-20T07:20:52.344500Z

if you have some reproducible code I could probably tell you

thheller 2021-04-20T07:21:03.344700Z

but without its kinda hard to say

grounded_sage 2021-04-13T17:15:34.158600Z

yes it fixed it for this one actually. It was the :browser target for the panel UI that is still throwing the source map error.

thheller 2021-04-13T18:40:14.159200Z

@dannyfreeman you can do it a little cleaner by setting :compiler-options {:force-library-injection ["es6/util/arrayfromiterator"]}. the problem is that the polyfill detection logic somewhere makes a mistake and that part gets lost but you can always just tell it which polyfills to include

👀 1
thheller 2021-04-13T18:42:19.159600Z

they are all listed here https://github.com/google/closure-compiler/tree/master/src/com/google/javascript/jscomp/js

thheller 2021-04-13T18:43:09.160100Z

so the above would just include https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/js/es6/util/arrayfromiterator.js which you did via the :prepend

lispers-anonymous 2021-04-13T18:51:49.161500Z

I just tried to use :force-library-injection ["es6/util/arrayfromiterable"] in my compiler-options and that did not work. arrayfromiterator also didn't change anything but that function is available already.

lispers-anonymous 2021-04-13T18:58:43.162100Z

Is it possible that arrayfromiterable needs to be included here: https://github.com/thheller/shadow-cljs/blob/9a4144b65141ecdacc28fefed53fe968035363e5/src/main/shadow/build/closure.clj#L2522

thheller 2021-04-13T19:00:08.162600Z

oh right. force-library-injection only triggers for release builds

thheller 2021-04-13T19:00:19.162800Z

I guess that could be adjusted too yeah

lispers-anonymous 2021-04-13T19:00:44.163100Z

I will try this on a release build real quick

lispers-anonymous 2021-04-13T19:04:52.163900Z

Yeah that :force-library-injection key works for my release build, but not a development build.

thheller 2021-04-13T19:23:28.164100Z

https://github.com/thheller/shadow-cljs/issues/870

lispers-anonymous 2021-04-13T19:27:17.164400Z

Thank you 🙂 is there anyway I could help out with that?

thheller 2021-04-13T19:40:32.164600Z

too late 🙂 should be fixed in 2.12.5. also just added the interable by default so should be ok even without config.

lispers-anonymous 2021-04-13T19:43:55.164800Z

Awesome. I'll be on the lookout for that release. Thank you very much for the help.

lispers-anonymous 2021-04-13T19:45:50.165Z

I was able to accomplish this by writing my own preloads and using those in the test build.

lispers-anonymous 2021-04-13T19:48:01.165200Z

If running clojure code won't work for you, you should be able to require the js file in the namespace declaration of your preload file.

valsen 2021-04-13T20:44:21.169600Z

Hi, noob question here. What is a common way to set and access env variables, for example to use different firebase configs for dev/prod? I’ve tried using js/process.env but get a ReferenceError: process is not defined. Is there some trick to getting this to work in shadow or cljs in general?

valsen 2021-04-14T14:19:01.189600Z

I’ve configured my env variables and added them to my shadow-cljs.edn as described in those links, but how can I now reference/use them in my cljs code? For example, if I have :closure-defines {app.core/APIKEY #env "APIKEY"} , I don’t understand how to reach it from within the app.core namespace

valsen 2021-04-14T14:28:52.189800Z

If I try something like {:api-key #shadow/env "APIKEY"} I get a compile error No reader function for tag shadow/env . And simply referring to it by its name, APIKEY , I get an undeclared var error

thheller 2021-04-14T15:06:39.190200Z

you (goog-define APIKEY "default value") in app.core and then use it like any other clojure def

valsen 2021-04-14T15:17:41.190400Z

Ok, thanks:) It does however feel a little strange having to use system environment variables for setting config values for the frontend. Isn’t it more common do that in runtime instead of when compiling?

thheller 2021-04-14T16:13:46.190600Z

yes, but a browser environment will hardly have access to your system environment

thheller 2021-04-14T16:13:58.190800Z

so either way it is something that will become part of your build at compile time

thheller 2021-04-14T16:14:38.191Z

I do recommend keeping some kind of configuration in your HTML directly, so that it becomes actual "environment" configuration and is not hardcoded at build time