Fork me on GitHub
#shadow-cljs
<
2018-08-09
>
richiardiandrea04:08:05

How does shadow handles npm transitive deps? Core has deps.cljs I wonder whether I can rely on it in shadow

justinlee04:08:14

What do you mean? npm handles npm transitive deps

richiardiandrea04:08:05

@lee.justin.m say you have a cljs dep that includes an npm dep. You want to consume that in shadow

justinlee04:08:41

you have to add it to your package.json. e.g., reagent requires react. you have to add react to your package.json and install it via npm

justinlee04:08:20

that’s why there is the whole cljsjs shim thing

richiardiandrea04:08:13

Uhm, I thought this was a thing in shadow as well, but maybe Thomas was against it and therefore not implementated: https://dev.clojure.org/jira/browse/CLJS-1973

richiardiandrea04:08:01

In your scenario a user of many lib should know and include all the npm transitive deps manually

justinlee04:08:22

i’m not 100% sure what he’s talking about, but i think npm-deps actually shells out to npm

justinlee04:08:35

in shadow, you need to install npm modules yourself, which you can do via npm or yarn

richiardiandrea04:08:46

So yeah, that means that I would need to know what npm deps each lib is using. Example, cuerdas uses xregexp, if I want to use it I need to install it manually?

richiardiandrea04:08:35

Yeah well the answer seems yes

richiardiandrea04:08:50

It feels a bit backwards

justinlee04:08:55

it’s great 🙂

justinlee04:08:45

having manually packed npm dependencies is fine when you have one-offs, like in cuerdas. but the moment you start including, say, more than one react library, it’s a nightmare

richiardiandrea04:08:43

Say you are using 100 cljs libs including 100 npm deps...I do not think this method scales 😄

justinlee04:08:59

on the contrary, it’s the only method that would scale

justinlee04:08:20

you would never get 100 cljs libs with 100 npm deps to work if they all had hardcoded crap installed

richiardiandrea04:08:36

Maybe I am missing something...

richiardiandrea04:08:48

What does hardcoding means?

richiardiandrea04:08:08

Each cljs dep declares what it is using

justinlee04:08:16

cuerdas, for example, literally includes the javascript in its distribution. so if some other library has xregexp, you’ll get two copies

richiardiandrea04:08:38

Yep that is not good ok

justinlee04:08:53

in some cases, like react, it just won’t work

richiardiandrea04:08:12

Vanilla cljs allows an :npm-deps in deps.cljs in your jar

justinlee04:08:18

npm has a rather sophisticated algorithm to flatten the tree

richiardiandrea04:08:38

So that you can transitively know which npm dep you will be using

richiardiandrea04:08:51

And shell out to npm install

richiardiandrea04:08:15

Cuerdas' current way is not optimal if it does the bundling and I agree with you there

justinlee04:08:20

yea i don’t know what npm-deps is doing under the covers (I understand that it uses npm, but beyond that, it’s a mystery to me)

justinlee04:08:46

i think at this point in time, most libraries (every one that I’ve looked at) that includes npm stuff just bundles the npm in the distribution

richiardiandrea04:08:39

Yeah that's bad, but: > ClojureScript libraries that package foreign dependencies can also benefit from these enhancements. Ticket CLJS-1973 adds support for the :npm-deps option in deps.cljs files, allowing library authors to develop and distribute libraries that directly depend on Node.js modules.

richiardiandrea04:08:03

This is how it should be done if I understand correctly

richiardiandrea04:08:32

Not sure though 😉

justinlee04:08:12

there are a couple of things to know (that aren’t obvious from that article or the official docs). (1) npm-deps will run the npm module through advanced compilation, which may or may not work, and (2) it seems incredibly brittle and basically impossible to debug. I believe that is why dnolen published the webpack guide

richiardiandrea04:08:47

Agree and switched to shadow for exactly those reasons

richiardiandrea05:08:13

Still, a way to declare and detect transitive npm deps seems good

justinlee05:08:45

it would be interesting if libraries could just ship with their own package.json

justinlee05:08:55

then you could just do normal transitive resolution

richiardiandrea05:08:07

But that's what the deps.cljs is for I guess

richiardiandrea05:08:20

And note "I am guessing"

richiardiandrea05:08:29

Will actually try it out tomorrow

justinlee05:08:32

i’m not sure deps.cljs will ever be expressive enough, but maybe

justinlee05:08:40

actually it should work yea

justinlee05:08:04

whether shadow can or should interpret npm-deps is something thomas would have to answer

justinlee05:08:08

that makes my head hurt 🙂

richiardiandrea05:08:40

Ah ah! I hear you simple_smile

richiardiandrea05:08:03

Cool so I'll stay tuned, thanks for answering 😉

lilactown05:08:58

I’m pretty sure shadow-cljs respects deps.cljs

lilactown05:08:09

seeing as I installed a cljs dependency just now and a bunch of stuff magically appeared in my package.json when I did git status 😄

justinlee05:08:12

@lilactown whoa. oh fascinating. so you installed a cljs dep that had an npm-deps in it, and shadow added that to your package.json?

justinlee05:08:17

what dep did you add? I want to test

justinlee05:08:56

yea look at that!

🎉 8
justinlee05:08:09

@richiardiandrea there’s your answer. it does respect deps.cljs

richiardiandrea05:08:40

Awesome @lilactown @lee.justin.m with this answer in mind my goal is to check if it works with cuerdas and fix it if not. Thank you!

thheller06:08:21

@richiardiandrea deps.cljs works BUT in case of the shadow-cljsjs compatibility packages no deps.cljs is provided. that is because there is no way to selectively install :npm-deps. Since this package contains a lot of cljsjs.* shims adding a deps.cljs would always install too many packages. https://github.com/thheller/shadow-cljsjs

thheller06:08:03

cuerdas is one of those cljsjs/foreign-libs compat things

thheller06:08:12

therefore its xregexp dep is not installed

thheller06:08:34

FWIW the thing I don't like about deps.cljs and :npm-deps is about their interaction with the compiler. using them to declare actual npm dependencies is fine by me

urbanslug11:08:51

is there a cljs way of reading/setting env vars before starting the dev server?

thheller11:08:22

not sure I understand that question

urbanslug11:08:37

So I want to set some env vars that my cljs app should read to know e.g which API url to make requests to

urbanslug11:08:55

also have defaults

thheller11:08:07

assuming your app is :target :browser?

thheller11:08:27

ok what I always recommend is forgetting about env vars

thheller11:08:34

the browser does not have env vars

thheller11:08:19

take this setup for example

thheller11:08:45

it is absolutely fine and recommended to pass whatever "env" vars you want to the init method

thheller11:08:14

ie. call <script>starter.browser.init({url:""});</script>

thheller11:08:22

and do whatever with that in the code

thheller11:08:42

so the HTML file controls the env

urbanslug11:08:57

but this var changes from dev to prod

thheller11:08:20

thats exactly the point. create a different html depending on env

thheller11:08:54

the problem with feeding env vars to the compiler

thheller11:08:04

is that you actually have to recompile to get the changes in

thheller11:08:43

you can also use :closure-defines {some.app/var #shadow/env "MY_VAR"} if you are deadset on using environment variables

thheller11:08:17

but I really really do not recommend doing so.

urbanslug11:08:31

I would expect one sets them only once when starting dev server or while doing advanced compilatiion so recompiling to get the changes in doesn't sound like a problem since as the closure defines describes it they are compile time constants

urbanslug11:08:01

the list of vars can be large

thheller11:08:30

well .. if they are actually compile time constants then use :closure-defines

thheller11:08:48

but often they are actual runtime configuration data

thheller11:08:14

and keeping them out of the compiler has many benefits, like being able to easily change them

urbanslug11:08:15

hmmm yeah they are runtime config data

thheller11:08:00

to me setting things in the HTML is the equivalent to what env vars usually do

thheller11:08:10

since they control the env

thheller11:08:20

doing it in the compiler is just weird

urbanslug11:08:25

ok here's a situation. I set it in the html and read it in my init fn then from there I use a setter to create a sort of global for the app object that other functions can access

thheller11:08:53

well don't you have a global state atom anyways?

thheller11:08:09

eg. reagent, re-frame or whatever? all of them pretty much do

thheller11:08:26

just put it in there in the init fn

thheller11:08:25

otherwise a (my.app.env) (defonce config (atom {:the "defaults"})) also works

urbanslug12:08:39

ya I'll just swap! the app state

urbanslug12:08:55

I'm wondering what could go wrong with two index.html files

urbanslug12:08:07

not two at the same time but...

thheller12:08:25

whats your server side?

urbanslug12:08:37

I expect to point nginx to get pointed to the index.html file of my choice

urbanslug12:08:45

but the SRE guys could feel different

thheller12:08:04

well you can just use a build hook to generate a proper index.html

urbanslug12:08:09

*index.html meaning intial html file

thheller12:08:46

or anything else you do to deploy really

urbanslug12:08:56

I was wondering whether to use something like a build hook or sed to set the necessary vars in the index.html file

urbanslug12:08:00

so that I have one

thheller12:08:01

if if you intended to call MY_ENV=foo shadow-cljs release your-build

urbanslug12:08:25

yeah that would be great

thheller12:08:28

you can call shadow-cljs release your-build && generate-prod-index.sh

thheller12:08:17

you can totally use env vars if you want to ... I just don't recommend it. up to you what you do

urbanslug12:08:49

yeah the generate-prod-index.sh would edit the html file to have the necessary values for vars

urbanslug12:08:13

and then init function would read them

thheller12:08:40

I know a few people use the clj-run option

thheller12:08:20

(defn release []
  (shadow/release :my-build)
  (generate-prod-index))

thheller12:08:32

shadow-cljs run my.build/release

thheller12:08:51

if you want to stay in clojure and not resort to other shell scripts

urbanslug12:08:22

ah seems perfect for my case

urbanslug12:08:46

actually this is really good. It answers a lot of questions that I had

thheller12:08:44

you can also split the shadow/release call into calling (shadow/get-config :my-build) updating that config (its just a map) and then calling shadow/release* with it

thheller12:08:16

so you could adjust the config pretty much any way you like

lilactown14:08:45

is there a way to get closure to chill out about This code cannot be converted from ES6. extending native class: Array

lilactown14:08:26

I’m trying to mess with the compiler options and adjust what it expects as input/output, but it’s not working quite yet

thheller14:08:33

gonna need more info. never seen this message.

lilactown14:08:56

a library I’m trying to do use does something akin to:

class Foo extends Array {
  ...
}
which Google Closure cannot convert to ES5

lilactown14:08:19

their advice is basically that they only support ES6

thheller14:08:06

which library?

lilactown14:08:12

npm install pts if you want to test it

thheller14:08:26

the package doesn't contain the sources. only a pre-bundled webpack npm package

thheller14:08:41

wish people would start putting proper ES6 sources in their packages 😞

lilactown14:08:26

it’s hard to get people to understand what to bundle/transpile vs not

thheller14:08:01

its easy: transpile anything that isn't standard es6 but not more

lilactown14:08:16

I had to tell a bunch of contractors at work that no, you cannot deliver a bunch of internal npm packages that have JSX in them 🙃

thheller14:08:17

well I guess for maximum compatibility you also want a commonjs package

thheller14:08:06

so es6 in node_modules usually gets transpiled by babel for reasons such as yours

thheller14:08:27

but the file is not properly identifed as es6 since it doesn't have any import/export statements

thheller14:08:51

thats my mistake probably, closure probably identifies it properly when pre-parsing

lilactown14:08:49

hm. this looks like a case where both babel and closure fail

thheller14:08:19

I have not yet figured out how to stop closure from transpiling

thheller14:08:43

for some reason :language-out :ecmascript6 still transpiles most of the stuff out

thheller14:08:48

when there isn't actually a reason to do so

thheller14:08:39

are you sure that babel fails?

thheller14:08:12

> Yes, Pt extends Float32Array so that we can keep all the native functions while adding new ones.

lilactown14:08:22

it’s not babel AFAIK

[:workspaces] Compiling ...
Closure compilation failed with 1 errors
--- node_modules/pts/dist/pts.js:288
This code cannot be converted from ES6. extending native class: Array

thheller14:08:22

ah the good old OOP mentality 😉

thheller14:08:44

@lilactown yes its not babel. I mean did you try babel manually since you said "babel and closure fail"

thheller14:08:54

shadow-cljs currently does not try to use babel for that file

lilactown14:08:56

oh I see what you mean. there might be a babel plugin we can use, but usually babel also cannot handle this

lilactown14:08:04

he mentions it in the issue

thheller14:08:13

babel seems to work fine

thheller14:08:58

well not sure if the package actually becomes usable but you can try it

lilactown14:08:08

:thinking_face: so should I vendor the lib and run babel on it, since shadow isn’t detecting that it’s ES6?

thheller14:08:34

no. I'll fix the detection part but first I want to verify that it actually works

thheller14:08:04

since I have no idea what pts actually does I could use some help with the verify part

thheller14:08:07

what you want to do is: in your project dir run npx babel --presets env node_modules/pts/dist/pts.js -o node_modules/pts/foo.js

thheller14:08:25

and then (:require ["pts/foo" ...]) instead of just pts

thheller14:08:58

hmm ok doesn't actually work

thheller14:08:02

TypeError: Constructor Float32Array requires 'new'
    at Pt.Float32Array (<anonymous>)

thheller14:08:27

@lilactown seems to work if you set :js-options {:language-out :ecmascript6}

thheller14:08:07

seems like it just can't be compiled down properly

lilactown14:08:57

:face_palm: I was trying to set that in :compiler-options

thheller14:08:35

oh right. I don't think its properly documented for :js-options

thheller14:08:02

:compiler-options is only for the final :advanced optimization

thheller14:08:14

but since the JS sources don't go through that they have their own setting

lilactown14:08:48

oof yeah this library is painfully OO 😞

wilkerlucio15:08:30

and docs sounds kind nice, could be cool to have a functional cljs wrapper around it

lilactown15:08:07

yeah I’m going to get something working and then maybe start hacking on a CLJS interface

lilactown15:08:12

the library just looks freakin’ cool

urbanslug16:08:37

@thheller Do I need to do anything to turn this JS object into a clj(s) obj <script>starter.browser.init({url:""});</script>

urbanslug16:08:57

because in my case it's undefined in the cljs

urbanslug16:08:37

nvm I wasn't using json

thheller17:08:50

@urbanslug dont need to use json. js->clj it fine or just normal js interop

currentoor17:08:04

has anyone used shadow-cljs to develop on a raspberry pi?

thheller17:08:48

why would you do that to yourself? thats just gonna be way too slow 😉

thheller17:08:03

I mean running code on it is fine

thheller17:08:09

but developing on it?

currentoor17:08:52

well the approach i was thinking was running shadow-cljs + emacs on my laptop and pushing the compiled js to the raspberry pi

thheller17:08:01

yeah thats fine

currentoor17:08:19

is there a hook somewhere in shadow-cljs to do this sort of thing?

currentoor17:08:41

ideally i’d have a dev environment with hot code reloading

thheller17:08:05

well what do you want to build? assuming a node-script?

currentoor17:08:08

the only hooks i see are inside JS

currentoor17:08:15

yes a node script

thheller17:08:59

how is the pi connected?

thheller17:08:11

can you mount a network drive on it?

currentoor17:08:14

ssh at the moment 😅

thheller17:08:21

sshfs or something like that would work too

currentoor17:08:45

yeah i could do that

thheller17:08:00

ah damn that would probably suffer the same issue I fixed yesterday

thheller17:08:09

didn't release the fix yet though

thheller17:08:01

well what I would probably do a) mount a sshfs drive on the pi that points to your local project

thheller17:08:15

b) run node out/your-thing.js on the pi

thheller17:08:50

that will connect to your local machine for the hot-reload/repl stuff

currentoor18:08:07

cool, what’s the issue you fixed?

currentoor18:08:11

and thank you!

thheller18:08:26

well :node-script currently has some hard coded absolute paths

thheller18:08:54

so they basically only run on the machine they were compiled on

thheller18:08:01

during development, release is fine.

thheller18:08:14

but yeah the pi won't find the absolute path

thheller18:08:17

so that doesn't work

currentoor18:08:38

i see, so the above approach won’t work yet right?

thheller18:08:45

right now no

thheller18:08:50

I should have a release ready soon

currentoor18:08:01

ok no worries simple_smile

currentoor18:08:37

also congrats on the clojurists together grant, well deserved!

thheller18:08:56

thx although I'm beginning to think I scammed myself

💸 4
thheller18:08:10

spending way more time with this than I planned initially 😉

thheller18:08:31

but its good to work on the stuff I wanted to work on for a while

currentoor18:08:58

it’s definitely good for the community simple_smile

urbanslug18:08:53

More of a cljs question. I set a var as you said @thheller but when it comes to retrieving it from the app-state does something change? all my attemps to retrieve give me null/undefined

thheller18:08:53

how are you setting it?

urbanslug18:08:07

(-> @app-state :config :url) however it's there when I console log the (:config @app-state)

urbanslug18:08:22

(swap! app-state merge {:config (js->clj config)})

urbanslug18:08:45

where config is the var from js

thheller18:08:58

I'm confused. is it there or not?

thheller18:08:03

its there if you do (:config @app-state) but not when (-> @app-state :config :url)?

urbanslug18:08:35

or even (:url :config @app-state)

urbanslug18:08:40

the threading doesn't matter

thheller18:08:40

thats not valid

thheller18:08:51

(get-in @app-state [:config :url]) doesn't work?

thheller18:08:11

same as threading so it shouldn't be there either then

thheller18:08:36

ah wait ...

thheller18:08:01

the (:config @app-state) probably shows {"url" "..."} right?

thheller18:08:09

thats a string key and you are trying to get a keyword

thheller18:08:37

try either (get-in @app-state [:config "url"]) or (js->clj config :keywordize-keys true) when doing the initial swap!

urbanslug18:08:31

> thats a string key and you are trying to get a keyword