shadow-cljs

https://github.com/thheller/shadow-cljs | https://github.com/sponsors/thheller | https://www.patreon.com/thheller
zimablue 2021-05-13T04:53:15.158100Z

ah ignore me I found a link in this chat somewhere

Endre Bakken Stovner 2021-05-13T08:46:34.162600Z

When using the {:js-options {:js-provider :require}} the luminus landing page tells me that the javascript has not compiled and I get these errors in the developer console:

app.js:1672 An error occurred when loading shadow.js.shim.module$react.js
env.evalLoad @ app.js:1672
app.js:1673 ReferenceError: require is not defined
    at eval (:3000/js/cljs-runtime/shadow.js.shim.module$react.js:3)
    at eval (<anonymous>)
    at Object.goog.globalEval (app.js:577)
    at Object.env.evalLoad (app.js:1670)
    at app.js:1773
env.evalLoad @ app.js:1673
app.js:1672 An error occurred when loading shadow.js.shim.module$react_dom.js
env.evalLoad @ app.js:1672
app.js:1673 ReferenceError: require is not defined
    at eval (:3000/js/cljs-runtime/shadow.js.shim.module$react_dom.js:3)
    at eval (<anonymous>)
    at Object.goog.globalEval (app.js:577)
    at Object.env.evalLoad (app.js:1670)
    at app.js:1811
env.evalLoad @ app.js:1673
util.cljs:187 Installing CLJS DevTools 1.0.3 and enabling features :formatters :hints :async
app.js:1672 An error occurred when loading everclear.app.js
env.evalLoad @ app.js:1672
app.js:1673 TypeError: Cannot read property 'prototype' of undefined
    at Object.reagent$impl$component$create_class [as create_class] (component.cljs:323)
    at Object.reagent$impl$component$fn_to_class [as fn_to_class] (component.cljs:373)
    at Object.reagent$impl$component$as_class [as as_class] (component.cljs:379)
    at reagent$impl$template$reag_element (template.cljs:159)
    at Object.reagent$impl$template$vec_to_elem [as vec_to_elem] (template.cljs:284)
    at Object.reagent$impl$template$as_element [as as_element] (template.cljs:288)
    at Object.eval [as reagent$impl$protocols$Compiler$as_element$arity$2] (template.cljs:305)
    at Object.reagent$impl$protocols$as_element [as as_element] (protocols.cljs:5)
    at f (dom.cljs:47)
    at Object.reagent$dom$render_comp [as render_comp] (dom.cljs:19)
env.evalLoad @ app.js:1673
This happens even though lein shadow watch app builds and compiles just fine. Any hints for how to debug it?

2021-05-13T08:56:26.162900Z

@endrebak85 Why do you use :js-provider :require? This is option isn't intended for code that should run in the browser.

Endre Bakken Stovner 2021-05-13T08:57:08.163100Z

Ah, I want to use some npm libraries that do not compile with the regular settings.

2021-05-13T08:58:25.163300Z

It seems like :js-provider :require isn't the right solution for that. What error do you get with the regular settings?

Endre Bakken Stovner 2021-05-13T09:00:00.163500Z

[:app] Compiling ...
[:app] Build failure:
Closure compilation failed with 2 errors
--- node_modules/d3-array/dist/d3-array.js:280
This code cannot be converted from ES6. extending native class: Map
--- node_modules/d3-array/dist/d3-array.js:300
This code cannot be converted from ES6. extending native class: Set

Endre Bakken Stovner 2021-05-13T09:00:33.163700Z

With :require it compiles (but does not work in the browser)

Endre Bakken Stovner 2021-05-13T09:02:18.164Z

I am a complete JS-n00b, but I tried to follow the guide https://shadow-cljs.github.io/docs/UsersGuide.html#_using_npm_packages.

2021-05-13T09:05:59.164200Z

Well, the JS ecosystem is a mess 🙂 JS is constantly extended with new language features, and the Google Closure compiler doesn't understand all of them. The library d3-array apparently uses features that the Google Closure compiler doesn't understand. What I would try is to load d3-array separately in the browser and use :js-options {:resolve {"d3-array" {:target :global }}} https://shadow-cljs.github.io/docs/UsersGuide.html#js-resolve-global. You'll probably have to process d3-array for the browser as well. Usually people use Babel for that.

2021-05-13T09:06:32.164400Z

See also https://github.com/d3/d3-array/issues/87

2021-05-13T09:07:40.164700Z

You might alternatively get away with an older version of d3-array

2021-05-13T09:09:05.164900Z

Notably d3-array also appears here as an example for a JS module that tends to need special treatment for the browser: https://github.com/babel/babel-loader#some-files-in-my-node_modules-are-not-transpiled-for-ie-11

Endre Bakken Stovner 2021-05-13T09:10:39.165200Z

Hmmm, d3-array is a dependency of some other library, so trying to choose a specific library version manually sounds like it might easily lead to problems. Thanks for the pointers. I will try to understand them now :)

2021-05-13T09:12:58.165400Z

Maybe :js-provider :shadow works. It could be the default already, I'm not sure, but that's something you could try easily.

thheller 2021-05-13T09:54:53.165800Z

@endrebak85 set :compiler-options {:output-feature-set :es6} not :js-provider

thheller 2021-05-13T09:55:21.166Z

if you set :js-provider :require shadow-cljs will NOT process any JS dependencies and instead expect a separate tool to do that. this is not what you want

thheller 2021-05-13T09:56:37.166300Z

the default :output-feature-set is :es5 but as the error is telling you the d3 code cannot be converted "down". so by setting a "higher" feature set that problem goes away

thheller 2021-05-13T09:57:23.166500Z

:js-provider :shadow is the default for :browser builds, do not change it unless you know what you are doing.

thheller 2021-05-13T09:58:03.166700Z

do not use :resolve either, that is not relevant here.

2021-05-13T10:04:48.166900Z

Interesting. So Google Closure is apparently quite good in supporting newer JS features. https://shadow-cljs.github.io/docs/UsersGuide.html#_output_language_options

thheller 2021-05-13T10:05:49.167100Z

yes, not super bleeding edge but quite good. usually when there is a standard there is support.

thheller 2021-05-13T10:06:38.167300Z

I might bump the default to :es6. really doesn't make sense to have :es5 still.

2021-05-13T10:12:16.167500Z

If you want support for IE11 you need to set that back to :es5 then, right?

thheller 2021-05-13T10:14:37.167700Z

yes

thheller 2021-05-13T10:18:35.167900Z

I just bumped the default. really should have done that a year ago 😛

2021-05-13T10:38:08.168100Z

@thheller https://cljs.github.io/api/compiler-options/language-out doesn't mention :ecmascript6 . Is that correct?

thheller 2021-05-13T10:39:46.168300Z

yeah .. doesn't matter. closure keeps changing the names of those. in shadow thats just an alias for ecmascript-2015, same option

1👍
Endre Bakken Stovner 2021-05-13T11:28:55.168600Z

Thanks for all the help 🙂 It seems to be working fine now.

zimablue 2021-05-13T13:50:11.175400Z

This is a question where I seem to have found a workaround, so please ignore if busy. shadow-cljs release node wasn't working, even though shadow-cljs compile node was. Following advice in the troubleshooting guide, I compared versions of the major dependent libraries across working and non-working projects and noticed that the com.google.javascript/closure-compiler version was different, someone had pinned it in the deps.edn with a note about "enable for node", I also noted that there is a specified shadow-cljs version in the deps.edn it eventually worked only when I both installed the specified version of shadow-cljs (through npm, how I'd originally installed), and unpinned the versions of com.google.javascript/closure-compiler and org.clojure/google-closure-library which had both been pinned under a note. The things I remain confused about: do aliases like: :aliases {:cljs {:extra-deps {thheller/shadow-cljs {:mvn/version "2.10.21"} enforce shadow-cljs version? From my troubleshooting, I guess not if you install via npm, therefore I should be installing via lein? Does anyone have an idea why these libraries have to be pinned, and shadow-cljs has to be an older version? the errors I got with the newer version were like this: No matching field found: getSourceName for class com.google.javascript.jscomp.JSError and The result of a goog.define call must be assigned as an isolated statement. one for pinned and one for unpinned closure compiler versions

Franklin 2021-05-13T14:31:14.178100Z

I have set up shadow-cljs to compile my cljs to a specific folder from where I'm serving them with a different HTTP server. Everything seems to work ok... code compiles on save and hot reloads on the browser 😃. However, I do not get a browser repl after the code is compiled when I run shadow-cljs watch app

Franklin 2021-05-13T14:31:59.178200Z

I have commented out ;; :dev-http {3000 "public"} in my shadow-cljs.edn file because my server is running on the same port

Franklin 2021-05-13T14:32:22.178400Z

I wonder what I might be missing?

Franklin 2021-05-13T14:34:13.178600Z

error messages also sometimes, on the browser console don't seem to pinpoint exactly where the error is.

thheller 2021-05-13T14:49:26.178900Z

if you use deps.edn and have a true-ish :deps key in shadow-cljs.edn then ONLY deps.edn controls the version for shadow-cljs you get

thheller 2021-05-13T14:49:40.179100Z

the one in package.json then only controls the version of the shadow-cljs command line tools

thheller 2021-05-13T14:50:34.179300Z

shadow-cljs cljs-repl app will give you the REPL

thheller 2021-05-13T14:51:00.179500Z

or use your editor to connect

Franklin 2021-05-13T15:58:36.179700Z

thanks, that worked

haywood 2021-05-13T16:08:39.181200Z

using shadow.loader/load but the XHR request it makes has this undefined segment in the url <http://localhost:3000/undefined/>&lt;correct shadow-cljs.edn asset path&gt; and I’m not sure why

haywood 2021-05-13T16:09:14.181500Z

have to look into this <http://shadow.loader.mm|shadow.loader.mm> = goog.module.ModuleManager.getInstance();

thheller 2021-05-13T16:09:25.181900Z

might be calling it too early? before it is initialized?

thheller 2021-05-13T16:12:28.182700Z

don't know why you posted the mm line? if that didn't exist you'd never get an URL from anything, it would error out differently

thheller 2021-05-13T16:12:54.183100Z

if you called shadow.loader/init without an argument that might be the undefined?

thheller 2021-05-13T16:13:40.183800Z

needs to get one argument for the prefix it is supposed to use, just an empty string for no prefix

haywood 2021-05-13T16:13:48.184Z

oh I was looking into what this was doing <http://shadow.loader.mm|shadow.loader.mm>.execOnLoad(id, cb)

haywood 2021-05-13T16:14:34.184600Z

I think it’s a user error that I need to sort out because I’m only getting the issue when I load the async route (in my app) directly

haywood 2021-05-13T16:14:49.185Z

but if I go to another route first that isn’t a separate module, and then go to async route it loads correctly

thheller 2021-05-13T16:15:36.185400Z

what do you want execOnLoad for? doubt anyone ever used that

thheller 2021-05-13T16:15:56.185800Z

I don't even know what its supposed to do 😛

haywood 2021-05-13T16:16:25.186400Z

oh… lol uhh I thought I was following the code for shadow.loader/load

shadow.loader.load = function(id, cb) {
  shadow.loader.ensureInitWasCalled();
  id = shadow.loader.string_id(id);
  if (cb) {
    shadow.loader.mm.execOnLoad(id, cb);
  }
  return shadow.loader.mm.load(id);
};

thheller 2021-05-13T16:23:39.187300Z

but why not just use load?

thheller 2021-05-13T16:24:06.187800Z

if you intent to call something when the code is loaded use :init-fn in the module config

haywood 2021-05-13T16:31:06.190300Z

hmm, interesting question. I’m trying to work the async module loading into my app’s router which is represented as data in state, so like, when the ‘on-navigate’ event fires I load the new page’s module and call it’s render function. I’m sure it could be improved like how you suggested, I’m trying to get a poc working

thheller 2021-05-13T16:32:39.190700Z

maybe you want something like shadow.lazy or the example from here https://code.thheller.com/blog/shadow-cljs/2019/03/03/code-splitting-clojurescript.html

haywood 2021-05-13T16:33:11.191300Z

ahh that is probably exactly it

thheller 2021-05-13T16:33:24.191800Z

the lazy-component util is using shadow.lazy https://github.com/thheller/code-splitting-clojurescript/blob/master/src/main/demo/util.cljs

haywood 2021-05-13T16:34:25.193100Z

["blog"
    {:name :<http://deli.pages.blog|deli.pages.blog>,
     :weight 5,
     :view #'deli-pages-blog/render,
     :label "blog",
     :controllers
     [{:identity
       (clojure.core/juxt :parameters :path-params :query-params),
       :start #(rf/dispatch [:fetch-blog])}]}]
the only reason it’s all bundled together is for importing the namespace and passing it to that :view keyword, so if I can just lazy reference that, bingo

haywood 2021-05-13T16:34:58.193400Z

thanks @thheller!!

2021-05-13T19:13:04.197Z

Any idea what I'm doing here. My shadow-cljs.edn

:source-paths ["src/main" "src/test"]

:builds {
   :test {:target :karma
          :output-to  "target/ci.js"}}
$ npx shadow-cljs compile test
shadow-cljs - config: /home/stuart/Source/cljs/cljs-asm/shadow-cljs.edn
shadow-cljs - connected to server
[:test] Compiling ...
[:test] Build completed. (62 files, 1 compiled, 0 warnings, 0.45s)
This puts a file ci.js in a folder named target I have a karma.conf.js
module.exports = function (config) {
    config.set({
        browsers: ['ChromeHeadless'],
        // The directory where the output file lives
        basePath: 'target',
        // The file itself
        files: ['ci.js'],
        frameworks: ['cljs-test'],
        plugins: ['karma-cljs-test', 'karma-chrome-launcher'],
        colors: true,
        logLevel: config.LOG_INFO,
        client: {
            args: ["shadow.test.karma.init"],
            singleRun: true
        }
    })
};
When I run karma start --single-run I see this:
13 05 2021 20:11:24.219:INFO [karma-server]: Karma v6.3.2 server started at <http://localhost:9876/>
13 05 2021 20:11:24.222:INFO [launcher]: Launching browsers ChromeHeadless with concurrency unlimited
13 05 2021 20:11:24.230:INFO [launcher]: Starting browser ChromeHeadless
13 05 2021 20:11:24.539:INFO [Chrome Headless 90.0.4430.212 (Linux x86_64)]: Connected on socket 63Mi3R7buKBGnu2WAAAB with id 43705700
Chrome Headless 90.0.4430.212 (Linux x86_64): Executed 0 of 0 SUCCESS (0.003 secs / 0 secs)
TOTAL: 0 SUCCESS
My tests are namespaced like this
(ns exfn.parser-test
  (:require [cljs.test :refer-macros [deftest is testing run-tests]]))
anyone any ideas why its not finding my tests?

2021-05-13T19:14:04.197500Z

I'm running karma from the root folder of my app, in teh same folder as karma-conf.js

thheller 2021-05-13T19:26:38.198100Z

hard to say. you omitted several important details. like which version is this? where is the actual test file?

2021-05-13T19:28:15.198700Z

sorry, the test files are in "src/test" folder off the root of my project.

2021-05-13T19:29:05.198900Z

Karma version is 6.3.2

thheller 2021-05-13T19:29:18.199200Z

shadow-cljs version? do you use project.clj or deps.edn?

2021-05-13T19:29:22.199400Z

yes

2021-05-13T19:30:51.200400Z

I'm using shadow-cljs via commands like

npx shadow-cljs watch app
npx shadow-cljs release app
etc

2021-05-13T19:30:55.200600Z

how do I make it show me the version ?

2021-05-13T19:31:54.201Z

shadow-cljs - config: /home/stuart/Source/cljs/cljs-asm/shadow-cljs.edn
=== Version
jar:            2.11.22
cli:            2.11.22
deps:           1.3.2
config-version: 2.11.22

thheller 2021-05-13T19:32:13.201200Z

do you use project.clj or deps.edn? is there a :lein or :deps key in your shadow-cljs.edn?

2021-05-13T19:32:37.201700Z

I only have a shadow-cljs.edn

2021-05-13T19:32:56.201900Z

My whole shadow-cljs.edn

;; shadow-cljs configuration
{:source-paths
 ["src/main"
  "src/test"]

 :dependencies
 [[binaryage/devtools "0.9.10"]
  [reagent "1.0.0"]
  [re-frame "1.2.0"]
  [day8.re-frame/re-frame-10x "1.0.1"]
  [org.clojars.ertucetin/re-frame-flow "0.1.1"]
  [bidi "2.1.6"]
  [com.rpl/specter "1.1.3"]]

 :nrepl    {:port 3333}
 :dev-http {8080 "public"}
 :builds   {:app {:target     :browser
                  :output-dir "public/js"
                  :modules    {:main {:init-fn <http://exfn.app/init|exfn.app/init>}}
                  :dev        {:compiler-options {:closure-defines {re-frame.trace/trace-enabled?        true
                                                                    day8.re-frame.tracing/trace-enabled? true}}}
                  :devtools   {:http-root "public"
                               :http-port 3000
                               :preloads  [day8.re-frame-10x.preload
                                           re-frame-flow.preload]}

                  :compiler-options {:closure-defines {re-frame.trace.trace-enabled? true}
                                     :silence-optimizations-warning true

                                    ;; in production so you can do an :advanced compile.
                                     :optimizations :simple}

                  :release    {:build-options {:ns-aliases {day8.re-frame.tracing day8.re-frame.tracing-stubs}}}}

            :release {:target     :browser
                      :output-dir "release/js"
                      :modules    {:main {:init-fn <http://exfn.app/init|exfn.app/init>}}
                      :compiler-options {:silence-optimizations-warning true
                                         :optimizations :advanced}}
            
            :test {:target :karma
                   :ns-regexp "-test$"
                   :output-to  "target/ci.js"}}}

thheller 2021-05-13T19:34:46.202700Z

looks fine. don't really need the :release build but thats not relevant to the test

thheller 2021-05-13T19:34:56.203Z

I assume you have actual deftest in your test file? with actual is assertions?

2021-05-13T19:36:04.204200Z

yeah, so i have a bunch of tests over 2 files. The tests are

src/test/interpreter-tests.cljs
src/test/parser-tests.cljs
A test looks like
(deftest is-register-tests?
  (is (true? (is-register? ":x")))
  (is (false? (is-register? "5")))
  (is (false? (is-register? "foo")))
  (is (false? (is-register? "b_010101"))))

thheller 2021-05-13T19:36:32.204700Z

don't know where you get :silence-optimizations-warning from but that config option does not exist

thheller 2021-05-13T19:36:39.205Z

those are not valid filesnames

thheller 2021-05-13T19:37:09.205600Z

(ns exfn.parser-test ... needs to be in src/test/exfn/parser_test.cljs

2021-05-13T19:37:31.206100Z

ah okay! I didn't know that, let me move them and try

thheller 2021-05-13T19:37:35.206300Z

you configured :ns-regexp "-test$" which does not match parser-tests

2021-05-13T19:37:55.206600Z

I thought that was parsing on the namespace, its parsing on the file?

thheller 2021-05-13T19:38:24.207Z

it is looking for the namespace, but that is derived from the filename

2021-05-13T19:49:24.207400Z

ok, cool. I'll move those files and try again.Thanks for your time and help!

macrobartfast 2021-05-13T20:05:46.209200Z

I am adding a server to my shadow project to handle endpoints and Crux. I would normally add Ring and Compojure to do that. Do I add these deps to shadow-cljs.edn or somewhere else? Are some of them already included… if so where are they pulled in? Thanks!

macrobartfast 2021-05-13T20:07:52.210Z

I created my app with create-cljs-app so that’s the structure.

thheller 2021-05-13T20:08:01.210300Z

I'd recommend using whatever you prefer for writing CLJ apps

thheller 2021-05-13T20:08:22.210900Z

I use lein for all my CLJ needs so I'll have a project.clj for that with the proper dependencies. so I'll have a shadow-cljs.edn for everything CLJS related and a project.clj for CLJ stuff

1👍
2021-05-13T20:11:06.211500Z

@thheller thank you! It works now and runs my tests. Fantastic 🙂

thheller 2021-05-13T20:11:45.212400Z

just pushed me to finish posting this 😉 might help a little in understanding why those names/paths matter

2021-05-13T20:13:04.213300Z

Nice, I have been confused about all the paths in clojure(script)

macrobartfast 2021-05-13T21:01:28.215300Z

in terms of adding a project.clj to a shadow project (next to a shadow-cljs.edn), I have (from lein new app server

(defproject server "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "<http://example.com/FIXME>"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "<https://www.eclipse.org/legal/epl-2.0/>"}
  :dependencies [[org.clojure/clojure "1.10.1"]]
  :main ^:skip-aot server.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})
how much of this is relevant pr meeds to be changed? (thanks for the hand holding).

thheller 2021-05-13T21:03:32.215800Z

I really can't guide you through building a CLJ server sorry

thheller 2021-05-13T21:04:29.216700Z

maybe follow some tutorial first and then integrate the files into actual project later

macrobartfast 2021-05-13T21:12:55.217400Z

I totally understand! And thanks for all the help… you’ve got me going in the right direction.