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.
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
that being said it does support websockets just fine, just probably not the ring variant or sente
@danvingo you can set :preloads
per module yes. just {:modules {:main {:init-fn <http://my.app/init|my.app/init> :preloads [foo.bar]}}}
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]}}
:preloads
are ALWAYS developmen only and do not apply to release builds
so just put it into modules like I said
perfect, thank you!
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..
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)
@i that means you are also using a conflicting too old core.async version as a dependency
with all AOT code you basically need to have the version that the AOT code used, otherwise you'll get errors such as that
I wonder why that makes a difference. With or without aot, I thought using an old library will both fail, not aot alone.
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
might also just work but either way you should ensure to use a compatible version
@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
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
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.
then you are likely just blocking yourself with your CSP rules?
https://github.com/homebaseio/datalog-console/blob/extension/shells/chrome/manifest.edn#L21
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)
ah sorry on my local that is set to 9630. Must have been a recent change I did not push.
I mean if you get warnings/errors already then they are the hint to fix it as well
you can paste some and I can maybe tell you what they mean
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.
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.
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
well is it actually running on 9630 or 9632? it is logged on startup
I now have it running on 9630. I'm not sure why it is different on fulcro-inspect. https://github.com/fulcrologic/fulcro-inspect/blob/3f1e2509c45712c15b7f3a2c64f878e412485286/shadow-cljs.edn#L15 https://github.com/fulcrologic/fulcro-inspect/blob/3f1e2509c4/shells/chrome/manifest.edn#L24
well if you have multiple shadow-cljs instances running locally and 9630 is taken it'll use 9631 then 9632 and so on
don't use :devtools-url
at all if you want to avoid that headache
ah wait no you have to
but yeah use your port ...
for source maps try :source-map-inline true
in :compiler-options
: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.
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
so stick with 9630 if that is the port you are starting shadow-cljs with
I was just following the configuration from fulcro-inspect
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
as for the REPL connection. open http://localhost:9630/runtimes
that will list all the connected runtimes with their respective ids
you can switch to a specific one but the default rule is to connect to the first one that connects
okay. We are doing similar things. Plan to also build an electron app so most of what was done there I thought would apply.
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.
inline should fix it but might be a bug in the :chrome-extension
target not supporting it properly or something
don't know, can't check right now
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
<anonymous> events.cljs:458
re_frame$fx$do_fx_after fx.cljc:59
... blah blah ...
<anonymous> 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?there is some issue with $jscomp
I havent been able to track down yet
normally it just goes away after wiping the cache and recompiling. just try deleting .shadow/builds/<build-id>
I attempted that, but it did not fix my issue
and then restarting the watch or release
hmm odd
It goes away when I target :es6
but I can't do that because of internet explorer 😞
I mean if you can make a reproducible minimal repro that would help a lot
IE is dead 😉
Not for my enterprise customers unfortunately lol
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.
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).
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?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"]}
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.
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.
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.
@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
Not sure if it makes a difference, but the modules it can’t find seems to be devDependencies
how are you running the tests? it is :node-test
I presume?
yes, using :node-test
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
there are ways to get it into the build of course but this would be the fastest and easiest
okay, it seems it could work, is there a way to make :autorun
run this as well though?
no there is not but I don't recommend using that anyways
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
that also gives you full control over how tests run and so on
Thanks for the tip, I’ll look into it! Just curious, what’s the reason behind the nested requires not working when using preloads?
it should work but what the code does actually matters and I don't have enough information to say
if you have some reproducible code I could probably tell you
but without its kinda hard to say
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.
@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
they are all listed here https://github.com/google/closure-compiler/tree/master/src/com/google/javascript/jscomp/js
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
well or rather https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/js/es6/util/arrayfromiterable.js
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.
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
oh right. force-library-injection only triggers for release builds
I guess that could be adjusted too yeah
I will try this on a release build real quick
Yeah that :force-library-injection
key works for my release build, but not a development build.
Thank you 🙂 is there anyway I could help out with that?
too late 🙂 should be fixed in 2.12.5. also just added the interable by default so should be ok even without config.
Awesome. I'll be on the lookout for that release. Thank you very much for the help.
I was able to accomplish this by writing my own preloads and using those in the test build.
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.
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?
https://shadow-cljs.github.io/docs/UsersGuide.html#closure-defines + https://shadow-cljs.github.io/docs/UsersGuide.html#shadow-env
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
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
you (goog-define APIKEY "default value")
in app.core
and then use it like any other clojure def
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?
yes, but a browser environment will hardly have access to your system environment
so either way it is something that will become part of your build at compile time
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