Fork me on GitHub
#shadow-cljs
<
2018-04-13
>
thheller08:04:53

ok .. seriously ... how can anyone say that clojure is slow ... doing a bit of work with react-native and expo and both are WAAAAAAAAAAAAAAY slower than shadow-cljs. especially when adding the ridiculous amount of time yarn add takes.

thheller08:04:28

@denik how are you reloading the code for the handler?

pez08:04:26

I have a situation where I can build and watch my app and it works in the browser, but can't connect a cljs-repl. It says this when I try to execute something from the prompt:

$ shadow-cljs cljs-repl app
shadow-cljs - config: …shadow-cljs.edn version: 2.2.29
shadow-cljs - connected to server
[1:1]~cljs.user=> (+ 1 2)
No application has connected to the REPL server. Make sure your JS environment has loaded your compiled ClojureScript code.
What am I not understanding?

thheller08:04:01

do you have the app opened in the browser?

pez08:04:22

I have my app opened in the browser, yes. (Not the UI, if that is what you mean.)

thheller08:04:50

what does the browser say? it should say something like shadow-cljs REPL connected in the console when you load the page

pez08:04:10

The server UI.

pez09:04:45

I don't get that message in the console. I get something about some custom cljs devtools formatters not being rendered and then the result of evaluating my app file (a clojure map).

thheller09:04:33

I'm confused

thheller09:04:52

you are running shadow-cljs watch app. you open whatever that produces in the browser?

thheller09:04:08

the formatters message is probably from cljs-devtools

thheller09:04:54

what is your build config?

pez09:04:11

I am sorry I am sharing my confusion. But you have the scenario right. Here's my build config:

pez09:04:10

:builds {:app {:target :browser
                :output-dir "resources/public/js/compiled"
                :asset-path "/js/compiled"
                :modules {:game-of-life {:entries [game-of-life.main]}}
                :devtools {:after-load app.main/render!
                           :http-root "resources/public"
                           :http-port 8080}}}

pez09:04:39

(It's a simple figwheel project that I am trying with.)

thheller09:04:28

and you have loaded in your browser?

thheller09:04:06

it should definitely say something about the REPL connecting

pez09:04:27

My index.html file looks like this:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" href="https://clojurescript.org/images/cljs-logo-icon-32.png">
  </head>
  <body>
    <div id="app">
      <h2>Static HTML from index.html</h2>
    </div>
    <script src="js/compiled/game_of_life.js" type="text/javascript"></script>
  </body>
</html>

thheller09:04:41

pretty sure thats loading the wrong file?

thheller09:04:03

at least I dont remember changing - to _ for the output files?

thheller09:04:28

can you check if both exist? maybe from a previous fighwheel compile

thheller09:04:45

just wipe resources/public/js/compiled to be safe

pez09:04:31

I'll check.

pez09:04:31

Yeah, both exist. Thanks!

pez09:04:37

That explains why the app worked even though shadow-clj said there were compilation errors. 😃 I'll fix and then I might have a Calva version that can handle shadow-cljs projects.

pez09:04:53

Hmmm, now the cljs repl starts and everything with that is dandy. But Calva still only finds the clj repl, so some more work is needed. But there's a whole weekend ahead to get it done! 😃

thheller09:04:19

by default there only is the clj-repl

pez09:04:19

Many thanks for your level of service, @thheller!

thheller09:04:39

you call (shadow.cljs.devtools.api/nrepl-select :app) to switch the current session over to cljs

thheller09:04:02

:cljs/quit will drop that session down to CLJ

pez09:04:57

Ah, but if I make two clones, maybe I can have both session types? It's a feature of Calva that it is easy to have both connected.

thheller09:04:22

yes, clone lets you do that

pez09:04:50

Another question, yesterday you said something about people using cljc files even though they only work in Clojure. What did you mean by that? Or if I misunderstood things completely.

thheller09:04:38

say you have a src/my/app.cljc (ns my.app) (js/console.log "foo")

thheller09:04:45

this will compile fine with CLJS

thheller09:04:56

but if you try to (require 'my.app) in a Clojure REPL it will blow up

thheller09:04:11

since CLJ does not understand the js/console.log

thheller09:04:29

same is true the other way. you can do clojure things that CLJS doesn't support

pez09:04:41

Isn't that what conditional readers are for?

thheller09:04:45

(ns my.app (:require []))

thheller09:04:52

yes they are.

thheller09:04:11

BUT not everyone that uses .cljc files actually makes sure that they work in both platforms

pez09:04:32

I see. That is partly why I want Calva to keep both repls available. So that it is easy to switch between them and try make sure they work in both worlds. It has been quite a bother to get tests working in cljs, so we write most of our stuff in cljc.

thheller09:04:40

yeah I tend to test more in CLJ as well. cljs.test is horrible to use, too many macros.

pez09:04:23

About clone now. With lein+figwheel I an ls-sessions and get a vector of sessions w/o cloning. But with shadow-cljs I must first clone to populate the vector. What I would think makes most sense is that the vector is populated to begin with, then I clone sessions that Calva can connect to.

pez09:04:50

It seems a bit strange to just clone nothing and magically there appears sessions.

thheller09:04:06

but thats how sessions are created

thheller09:04:41

with lein repl what you are cloning is the repl-y session which may already have put stuff in the session you don't actually need

thheller09:04:54

so cloning from nothing seems to be safer actually

thheller09:04:16

don't use lein repl as a baseline IMHO

thheller09:04:25

because clj won't have that either

thheller09:04:40

at least I think that is what happening

thheller09:04:50

figwheel doesn't actually create any sessions since only nrepl clients can do that

pez09:04:14

I hear you. Is there a way I can now that I am connected to a shadow-cljs clj repl?

thheller10:04:03

not really unfortunately

thheller10:04:22

nrepl doesn't exactly make it easy to give you extra information

thheller10:04:18

I could add a new nrepl op? so you send {:op "shadow-cljs/hello"} or so. shadow-cljs could reply with something while others will just reply with an error

pez10:04:28

That would work for me. Better than first testing (shadow.cljs.devtools.api/nrepl-select :app) and see if that works. I might start out with jsut letting the user manualy select what kind of session to connect to.

troglotit10:04:58

Is it expected that Ctrl-C on shadow-cljs watch app sometimes leaves server running, so next time shadow-cljs watch app doesn’t do anything?

thheller10:04:30

the server instance can be shared, so if you run multiple shadow-cljs watch only one server will be used

thheller10:04:59

the watch however should be stopped by the watch process that created it

thheller10:04:19

you run use shadow-cljs clj-repl (shadow/stop-worker :app) to stop it if its "stuck"

mjmeintjes10:04:14

Hi. I'm trying to get shadow-cljs to spit out a javascript file that is usable for a service worker. I'm re-using the :web-worker true setting inside shadow-cljs.edn. However, I'm getting the following problem: ReferenceError: document is not defined (`at hud.cljs:47`) when running the service worker. If this is a bug I'm happy to open a Github issue, just wanted to check that I'm not just doing something stupid.

thheller10:04:42

:web-worker in a module has a very distinct meaning. it is not suitable for making standalone webworkers or serviceworkers. that should be individual :target configs

thheller10:04:24

I'll get back to you later. need to eat something first.

mjmeintjes10:04:46

No rush, thanks. I've got it going in pure js for now in any case.

thheller11:04:01

I dont think CLJS is a good fit for service workers to be honest

thheller11:04:25

didn't do much with those yet but I think a dedicated framework like https://developers.google.com/web/tools/workbox/ seems to be better

mjmeintjes11:04:00

Yeah, that's exactly the library that I'm using now. Was thinking about moving it into clojurescript, but in retrospect you are probably right about just leaving in javascript. The only thing that I was thinking is about using macros to automatically get the list of files to pre-cache from manifest.edn and filesystem (instead of having to run the cli tool), but that is probably just going to overcomplicate things.

thheller11:04:26

yeah things could definitely be more declarative from the looks of it

thheller11:04:38

anyways .. brb

thheller11:04:54

@mjmeintjes I'm currently finishing up a target for react-native. will create simplified ones for worker after that, then we can properly evaluate whether it makes sense to use cljs for service workers

mjmeintjes11:04:35

Sounds great, thanks.

denik13:04:48

https://clojurians.slack.com/archives/C6N245JGG/p1523608108000180 @thheller initially I was using mount but I also tried a simple fn, saved the file and loaded it in the repl. Should that work?

thheller13:04:15

I mean HOW are you actually reloading the code

thheller13:04:30

load-file or require or tools.namespace?

denik13:04:10

load-file

thheller13:04:22

and what is the var? (defn foo [...])?

thheller13:04:35

I tested it in my REPL and it definitely works

denik13:04:56

Huh with shadowcljs embedded?

thheller13:04:22

(defn foo [req] {:status 200 :body "hello world"})

thheller13:04:49

when I change the :body, load-file and reload the browser I see the updated version

denik13:04:10

Hmm, tried that yesterday (several times) and it didn’t work for me. I’ll dig in. Thanks for the sanity check.

thheller13:04:35

@denik oops. it is actually broken.

thheller13:04:32

I tested by modifying the default handler which obviously works

denik14:04:44

did you pass in the default handler’s var as a symbol to http-handler?

thheller16:04:28

I tested by passing :http-handler dev.http/handle but the ns was demo.http/handle so I got a NullPointerException`. I added a warning for that.

thheller16:04:34

otherwise it works just fine

denik20:04:36

@thheller do you have a gist you can share? Is dev.http/handle the full ns or an alias? I’m wondering what I’m doing wrong. Also since I’m using shadow-cljs embedded, is there a way to pass the config in dynamically instead of through shadow-cljs.edn? I’m asking because the library I’m working on (releasing soon) is generating the shadow-config for the user and currently does some ugly macro magic to pretend that there is config when there is none. I wonder if that has anything to do with it.

thheller20:04:45

what does a macro have to do with a shadow-cljs config?

thheller20:04:40

shadow-cljs watch app basically calls (shadow.cljs.devtools.api/watch :app) which calls (shadow.cljs.devtools.api/watch* the-config-map {})

denik20:04:21

@thheller does watch start it’s own server? I’m currently calling this

(with-shadow-config
   (shadow-config config)
   (shadow-server/start!)
   (shadow-api/watch :app))

denik21:04:01

oh shadow-server/start! takes a config

denik21:04:47

@thheller I don’t see a shadow.cljs.devtools.api/watch*

thheller21:04:21

yeah sorry. I meant get-or-start-worker. just take a look at https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/cljs/devtools/api.clj should be easy to follow

thheller21:04:08

and no I do not recommend bypassing the config completely. that is not a supported use case since its waaaaaaaaaaaaaaaaaaaay too much work to account for that.

thheller21:04:33

and you lose a bunch of features by doing that anyways

thheller21:04:19

I'm curious to see what you are trying to build though

denik23:04:05

Thank you. The user can literally pass a regular shadow-cljs config (that is contained in a larger config) so they can still modify shadow-cljs at will but I merge defaults (that can be overridden) and therefore it’s important for me to be able to pass it dynamically.

denik01:04:48

@thheller something must have been off with lein-checkouts - was still running version 2.2.23 😕 this is absolutely not your fault. However to avoid this in the future, have you thought about printing the shadow-cljs version as the server is started (like in the command line)?

thheller07:04:05

seems like a good idea yeah

thheller13:04:59

wait no .. it works.

thheller13:04:47

used the wrong ns ... adding a warning for that 😛

denik14:04:39

Sorry I’m not sure I understand, add a warning for what?

bru15:04:29

Hello #shadow-cljs, I’m having a lot of fun with this tool. However, I’m a bit stuck while trying to create a macro on a project with browser target. Is that supported?

lwhorton15:04:11

what issue are you running into? i dont think shadow (or any build tool) affects the way your code is compiled wrt macros

lwhorton15:04:07

you certainly want to ensure any .clj files are still in your source-paths (its a common theme for people to do a /src/clj and /src/cljs

bru15:04:11

@lwhorton at the moment I’m trying just to do something naive like spitting out some hiccup, I just copy/pasted the example from here: https://github.com/thheller/shadow-cljs-examples/tree/master/macros

bru15:04:31

but even that doesn’t compile if I set target to browser

bru15:04:02

we’re talking about:

(defmacro h1 [props & children]
  `[:h1 ~props ~@children])

lwhorton15:04:08

how are you running that example proj?

bru15:04:31

@lwhorton shadow-cljs watch app (app is my build name)

bru15:04:46

it’s much bigger than that, I just never needed a macro so far

bru15:04:56

hm. I can try creating a skeleton app in fact

lwhorton15:04:22

i have a strong sense that you have some other configuration issue… i’ve never had issues with macros so long as you write them in a .clj file, and include them in a cljs file

lwhorton15:04:54

remember that as of 1.7? cljs automatically refer’s macros if both clj and cljs share a name

bru15:04:42

@lwhorton good to know, I’ll definitely start with the skeleton app then

thheller16:04:13

@bru what doesn't work? what error do you get?

bru16:04:56

Hi @thheller! I was getting two failing specs at compile time, hang on…

bru16:04:04

@thheller :

Call to clojure.core/defn did not conform to spec
-- Syntax error -------------------

  (... [{:keys (attrs)} ...] ...)
        ^^^^^^^^^^^^^^^

has extra input
and
-- Spec failed --------------------

  (... [{:keys (attrs)} ...] ...)
        ^^^^^^^^^^^^^^^

should satisfy

  vector?

thheller16:04:54

I guess it expects {:keys [attrs]}?

thheller16:04:41

thats clojure complaining about your code

bru16:04:03

right, but the only addition was really the defmacro… let me try to remove it again, one sec

bru16:04:18

@thheller confirmed, as soon as I remove this code, it compiles again:

(defmacro h1 [props & children]
   `[:h1 ~props ~@children])

bru16:04:31

@thheller hang on, I re-added without the @ and it compiled, it might have been just that

thheller16:04:36

that compiles fine and is not the cause

thheller16:04:04

is this a .cljc file?

bru16:04:34

nope, a .clj at the moment, then I have a .cljs that require-macroes it, I’m copying the example from https://github.com/thheller/shadow-cljs-examples/blob/master/macros

bru16:04:04

but I’m trying to make it work for a browser target rather than node

thheller16:04:12

target is irrelevant for macros

bru16:04:31

cool, that was my expectations, glad I was right in that 🙂

thheller16:04:57

just grep for {:keys (attrs)}. should be somewhere in your code

thheller16:04:12

OR you have another macro that emits this

thheller16:04:27

the h1 is not the cause

bru16:04:33

nah that was the first macro I attempted to add to this project. But it might be in some of the dependencies… checking now

bru16:04:39

and thanks for the support!

bru16:04:13

and bingo! I was requiring tagsoup in that namespace, and that seems to be the root cause of the error.

bru16:04:25

I suppose I’ll need a different parser

thheller16:04:31

> Thanks for reporting this, I'll be fixing it in a few days.

thheller16:04:37

famous last words in OSS 😉

thheller16:04:26

didn't the error tell you that it was that namespace? it should have. if not thats a bug.

bru16:04:25

fair point, let me check

bru16:04:50

right, indeed it does, although too deep in the stacktrace for me to notice. it’s much easier now knowing what to look for 🙂

thheller17:04:01

feel free to open a ticket with the tagsoup example. should be easy to reproduce when I find some time. the ns causing the problem should be the first thing you see not the last.

thheller17:04:12

will see I can make that work

bru17:04:07

awesome, thanks! Excellent work anyway, I’m really enjoying shadow-cljs

👍 4
Alex H17:04:00

was wondering - is there a way to see a breakdown of the final bundle size?

Alex H17:04:11

as in, which module/file/whatever is contributing what in terms of size?

lwhorton18:04:35

is there something fancy I should do to make sure my nrepl connection can attach to a shadow-cljs watch :id? i’m using vim-fireplace and I can’t seem to make a connectino

lwhorton18:04:32

i’ve tried using manual :Connect host/port/blah (vim), and also from a shadow-cljs cljs-repl using the api/nrepl-select :id .. but in the first case I get some unhelpful compilation error. in the second case I get cannot read prop 'nrepl_select' of undefined.

lwhorton18:04:14

do i have to start a watcher, then a cljs-repl, then connect my browser, then connect to my nrepl?

thheller18:04:44

only need to start the watch, then connect nrepl, then (shadow.cljs.devtools.api/nrepl-select :id)

thheller18:04:55

can't guarantee that the shadow alias exists

lwhorton18:04:13

must be some vim-fireplace issue i’m having. the watcher runs fine, an .nrepl-port file is spit out, and i can even connect a shadow cljs-repl

thheller18:04:50

I have zero clue what vim-fireplace does so I can't help there

lwhorton18:04:56

me too 🙂

lwhorton18:04:40

i don’t see this in the docs anywhere — but does shadow support a local-only profile kind of like leins ~/.lein/profiles.clj?

thheller18:04:28

~/.shadow-cljs/config.edn is merged into the main config yes.

lwhorton18:04:51

and now i can find it in the docs ><

thheller18:04:34

dont think I added it there yet

lwhorton18:04:05

> You can either configure this in your shadow-cljs.edn config for the project or globally in your home directory under ~/.shadow-cljs/config.edn. its nesteld in there, but could probably use its own sub-section

lwhorton20:04:37

so .. after much ado (digging through python, nvim support, pip, pyenv, pyvirtualenv) i’ve learned that from my root file /app/ I can invoke :Eval (+ 1 2) and the nrepl will return 3.

lwhorton20:04:00

if I go into /app/src/some-mod.cljs and attempt to Eval it will always give me a compiler error in the output

lwhorton20:04:55

it looks like that’s because I can actually eval any code that’s in a .clj file, but not cljs. which I suppose makes sense. I have to “start” the cljs environment via (cemeric.piggieback/cljs-repl :id), except when I do that I always get [:no-worker :main]

thheller20:04:38

that means the watch is not running

lwhorton20:04:14

hmm-- so i’m invoking clj-run build/dev where my builder does some stuff and ultimately defers to shadow/watch build-cfg opts

lwhorton20:04:42

so unless there’s something else happening when I use shadow-cljs watch id, i think it’s running?

thheller20:04:03

I'm a bit confused. You say (cemeric.piggieback/cljs-repl :id) which would return [:no-worker :id] not [:no-worker :main]

thheller20:04:15

so I'm guessing there might be some id mixup?

lwhorton20:04:16

sorry, i kept trying cemeric but also the shadow-provided api, I’m not sure if they returned different things let me check

lwhorton20:04:27

but it would be :main which is my module, i just used :id to confuse you

thheller20:04:18

things would be easier to follow if you just run shadow-cljs server

thheller20:04:20

connect to that

thheller20:04:15

run (shadow.cljs.devtools.api/watch :main) and then I think :Piggieback :main?

thheller20:04:34

leave the custom stuff out of it until that works. impossible for me to tell what you are doing otherwise

lwhorton20:04:18

ill keep churning

lwhorton20:04:35

interesting

lwhorton20:04:18

separate shadow-cljs server, then connect to the nrepl, then run /watch :main spits back “no build with id :main”-- and here’s my config

:nrepl {:port 8001}
 :builds {:app
          {:modules {:main {:entries [boot.core]}}
 ...

thheller20:04:38

yes beacuse the build id is :app

lwhorton21:04:34

ok, so now that i’m not being dumb — it spits back :watching. if from another buffer (cljs); i load the browser to connect to the js environment; I :Eval (shadow/nrepl-select :app); now i can eval in my cljs buffer

lwhorton21:04:27

i know there was a release earlier that made the api/watch <id> automatically start a server, right? is there something different about that server versus starting it at the command line shadow-cljs server i wonder?

thheller21:04:09

you mean clj-run?

thheller21:04:33

shadow-cljs watch app starts a server if it isn't running yes

lwhorton21:04:03

yes, sorry - my brain is all over the place today. i wonder if using clj-run to start a task that eventually calls shadow/watch config opts initializes a server differently

thheller21:04:55

if you are calling a function via clj-run that wants to call watch you have to do this: https://shadow-cljs.github.io/docs/UsersGuide.html#_calling_watch_via_clj_run

thheller21:04:17

{:shadow/requires-server true} this is the important bit

lwhorton21:04:53

i picked up on that a little bit ago while combing through the docs:

(defn dev
  {:shadow/requires-server true}
  [& args]
  (let [c (config/get-build! :app)
        build (merge c dev-cfg)]
    (move-index (:index-in build) (:index-out build))
    (shadow/watch build {:verbose true})))

lwhorton21:04:23

so that’s my shadow-cljs clj-run build/dev invocation

thheller21:04:34

watch only takes a keyword id, not the actual config

thheller21:04:21

what is dev-cfg?

lwhorton21:04:11

just a out-of-scope map that defines some configurations

lwhorton21:04:28

(def cfg {:index-in "resources/index.html"
          :index-out "public/index.html"
          :manifest "public/js/compiled/manifest.edn"})

(def dev-cfg (merge cfg {:compiler-options {:closure-defines {'build-tools.constants/graphql-uri (or (System/getenv "GRAPHQL_URI")

thheller21:04:30

and why is that not part of the config in the first place?

lwhorton21:04:46

because I needed dynamic access to env vars

thheller21:04:12

wouldn't it be easier to pass this into your app via the index.html maybe?

lwhorton21:04:29

so wrt the api/release there’s a release* version that does allow bypassing the get-config! calls

lwhorton21:04:48

i should have checked (and i thought I did) but there doesn’t seem to be a corresponding watch*

thheller21:04:02

yeah that doesn't exist for watch since that is slightly more involved when doing things

thheller21:04:30

but seriously .. why not pass the URL to your app on startup?

thheller21:04:26

change that to starter.browser.init("the-actual-url"); and have the init function set it to whatever var

lwhorton21:04:43

no particular reason, I just wanted to use closure.defines because that’s how i’ve done it in the past. but also you still need to provide that index.html file a dynamic env var somehow

lwhorton21:04:49

i guess it does get it out of the shadow-cljs world

thheller21:04:23

IMHO things like urls aren't actually compile time constants seeing as you are jumping through various hoops trying to get it there

lwhorton21:04:01

that’s a good idea i’ll look into this. that means I can just spin up a shadow server; nrepl works; the config goes back to being in one place

thheller21:04:04

generating a HTML file that contains the URL should be way simpler than messing with the entire build

lwhorton21:04:11

of course that means other people who dont want an nrepl now have to startup a server, then start up a watcher

thheller21:04:41

I'm still unsure what you are actually doing

thheller21:04:52

if you run shadow-cljs watch app the server will be running

thheller21:04:01

there is no need to start it seperately

thheller21:04:14

running it seperately however has certain advantages

thheller21:04:40

but you absolutely do not have to ...

lwhorton21:04:54

oh I see. I think I need to stop, slow down, and get some sleep 😕

lwhorton21:04:58

i’m asking really stupid questions

thheller21:04:07

... I reaaaaaaaaaaaaallly need to get this UI stuff working ... clicking a button to start a build will be sooo much easier

lwhorton21:04:22

you’re making a ui? didn’t know about that

thheller21:04:54

well ... planning to do a UI. did some sketches but I kinda don't like UI building

thheller21:04:58

doing too much of that for work

thheller21:04:20

but anyways the UX should go up 100x if you actually get a good UI

thheller21:04:27

an can click things for dev stuff

thheller21:04:19

but for now the way I recommend using interop with ANY editor

thheller21:04:30

is running shadow-cljs server and doing everything via the REPL

thheller21:04:38

FROM the editor

thheller21:04:12

cider recently added support for shadow-cljs and it seems to work pretty well

lwhorton21:04:38

oh they did? i was just digging around in there and didnt see anything