Fork me on GitHub
#cljs-dev
<
2020-04-10
>
orestis15:04:12

https://github.com/facebook/react/pull/18449 not sure if relevant, React seems to have switched to Closure compiler for compiling down to ES5

dnolen15:04:35

relevant for people who are militant about their dependencies (which is generally a good idea)

dnolen15:04:21

but not that meaningful for typical users prioritizing getting stuff done

lilactown16:04:49

it doesn’t look like they ship closure-y source so it’s not like we can treat it as non-external, right?

dnolen16:04:57

Not true, React is pretty Closure compatible

dnolen16:04:21

really all JS that doesn't do string based programming is already compatible

dnolen16:04:39

but lots of libraries do unnecessary dynamic stuff

dnolen16:04:38

so as long as people grab libraries willy nilly - then you're stuck w/ Webpack or whatever build mudball de jour

dnolen16:04:26

that said given how we've done things where we do have most of the stuff in place to DCE node_modules via Closure

dnolen16:04:42

so it doesn't matter if you use Webpack or whatever today

dnolen16:04:50

tomorrow you can use Closure

juhoteperi16:04:22

I haven't looked at the most recent React releases, but at some point React created bunch of methods and properties dynamically. Even if you don't use the methods from your own code, React uses ["foo"] when declaring them dynamically and .foo to refer to the same methods in other places, so externs are required to optimize React code. (Not sure if this is relevent for this discussion, just note about React being Closure compatible).

dnolen16:04:21

@juhoteperi yeah that was years ago at this point - I know that FB has been using Closure to optimize their own React usage for a couple of years now

dnolen16:04:50

I don't know if that forced them to solve that annoyance

dnolen16:04:53

but I'd be surprised

juhoteperi16:04:15

Yeah, the last related fixes to Cljsjs extern file where 3 and 2 years ago

juhoteperi17:04:05

But these problems weren't also very obvious to see, like 95% of Reagent tests passed and then a few test cases where broken due to these problems.

Roman Liutikov18:04:10

iirc, React team reached to Closure people at some point to get a couple of switches in the compiler that makes advanced mode less aggressive specifically for React

dnolen18:04:25

I think I addressed all the reported externs inference issues - let me know if I missed anything

dnolen18:04:41

if you all some low hanging fruit issues you'd like to see added to the next release let me know

dnolen19:04:05

3195, not now - 3086 approved

dnolen19:04:33

the others are too minor for now

👍 4
dnolen20:04:48

here are my current thoughts about the coming changes - https://gist.github.com/swannodette/919ef8fb51e7ee91b5f3ab643c4b3e55

dnolen20:04:23

feedback welcome, it's pretty simple - and I have another project that more or less confirms that it works

lilactown22:04:39

I can’t quite tell exactly what the motivation is yet. it sounds like it would be really useful for people who want to consume their CLJS code in storybook, or use with react-native. is that close to the mark?

Oliver George05:04:02

I'd be interested to see a working example if you do find a way to use Storybook with CLJS code.

dnolen22:04:00

@lilactown it removes nearly all the steps (some error prone) for people who want to use some random lib

dnolen22:04:27

but more importantly it provides a portable way to create clojurescript libs that depend on node_modules

dnolen22:04:36

that's not really possible today because there's no standard way to do it

dnolen22:04:17

the state of the art for creating CLJS libs that use JS deps is CLJSJS

dnolen22:04:34

and that's also error prone, requires maintenance

lilactown22:04:03

I see. agreed about cljsjs

lilactown22:04:01

I didn’t understand anything in that gist to be about clojurescript libraries. the bulk of the content seems to be about a :bundle target? I’m not sure yet how that rolls into libs

dnolen22:04:05

I suspect this already works for shadow-cljs but shadow-cljs isn't the standard tool chain so it's not really much of a win - not portable

👍 8
dnolen22:04:48

@lilactown because we don't need to talk about anything else other than :bundle

dnolen22:04:01

:npm-deps already exists and works

dnolen22:04:11

it was always a la carte

dnolen22:04:37

i.e. doesn't directly imply Closure, :nodejs target avoids that

lilactown22:04:21

hmm. I guess maybe I’m out of my depth? I don’t understand yet the link between a :bundle target that (by the gist) > any tool that handles Node.js `require` can build ClojureScript - webpack, metro, etc. to the problem of libs that depend on node_modules or using some random lib. I’m looking at this from several perspectives: • I’m a ClojureScript library author whose most popular libs depend on node_modules. How does this effect my libraries? • I’m an application developer who uses many node_modules libs and uses many ClojureScript libraries that wrap node_modules. How does this effect my toolchain and dev experience?

lilactown22:04:09

perhaps, if I’m inferring right, it means that instead of: webpack creates a big ball of JS that then GCC needs a bunch of externs / must export to the global context… it would go the other way: we would emit a bundle of CLJS code with holes, that expects the node_modules deps to be require(…)able and then feed that into webpack so that it can resolve the requires to node_modules and have webpack emit our final bundle

dnolen22:04:20

I'm a ClojureScript library author whose most popular libs depend on node_modules. How does this effect my libraries? this category does not exist

4
dnolen22:04:05

I'm an application developer who uses many node_modules libs and uses many ClojureScript libraries that wrap node_modules. How does this effect my toolchain and dev experience? fewer steps, less boilerplate when working the standard compiler

dnolen22:04:49

@lilactown right but nothing I said talks about webpack specifically - in fact not interested in automating that part at all

dnolen22:04:05

users will start webpack, fastpack, metro or whatever

dnolen22:04:46

this is a good area for people to extend ClojureScript to make a shadow-cljs like experience + Figwheel or custom build setup or whatever

lilactown22:04:41

that makes sense!

dnolen22:04:53

there are aren't any portable ClojureScript libraries that depend on node_modules today that don't require a lot of manual project-specific non-reproducible steps

dnolen22:04:41

if there were people wouldn't still be going to CLJSJS

lilactown22:04:11

that’s very true. I’ve had to maintain a separate branch of https://github.com/Lokeh/helix for cljs/figwheel-main, since the main branch implicitly relies on some behavior in shadow-cljs. it makes me feel bad, but it’s what I use both at work and for hobbies so it makes sense to spend most of my time with that

dnolen22:04:08

right that's basically the worse case scenario

dnolen22:04:22

so this is about establishing a normal way to do this - shadow-cljs supporting the standard way along w/ it's own way shouldn't be hard

dnolen22:04:52

libraries authors shouldn't have to care which thing is going to build their thing

lilactown22:04:38

yeah, my case isn’t exactly related to node_modules but it’s a sort of second-order effect

lilactown22:04:34

I also think that paving the pathway for consuming CLJS code from any JS bundler is a really good idea when it comes to incremental adoption. I was lucky with the projects I’ve been on the last few years, because we were able to start greenfield with CLJS.

lilactown22:04:24

trying a bottom-up incremental adoption of CLJS is a harder sell, since there’s so much boiler plate and additional stuff outside the happy path to get it running in an existing project using webpack or rollup or whatever the bundler du jour is

dnolen22:04:37

looking at your project, I would say that's directly related to the node_modules problem 🙂

dnolen22:04:56

because you can't do it w/o shadow you have to describe a whole bunch of other steps

dnolen22:04:04

@lilactown w/ my proposal above the caveats about non-shadow tools just go away

dnolen22:04:18

the npm step is also not necessary

dnolen22:04:50

you can add src/deps.cljs - the root of your classpath with those deps declared

dnolen22:04:05

then anyone can install the required npm deps, it doesn't matter what tool they finally end up using

lilactown22:04:34

I’ll do that now!

lilactown22:04:21

I still need to maintain a separate branch, unfortunately, due to some differences between how shadow and vanilla cljs handle JS in-project. that’s what I meant by second-order effects.

lilactown22:04:37

don’t need to get into that now, not relevant and I don’t completely understand it myself

dnolen23:04:07

huh, like Closure style JS in the project?

lilactown23:04:15

with shadow-cljs, I can use ES6 import/export but it doesn’t work as well with Closure-style JS (for some reason I haven’t spent the time to diagnose). with cljs, I couldn’t get the same ES6 import/export and require syntax to work, so I used goog.provide

dnolen23:04:21

huh - ok - honestly if you need some JS and you want it to always work for ClojureScript writing plain old Google Closure JS is probably wise - https://github.com/cognitect/transit-js/blob/master/src/com/cognitect/transit.js, is worth studying if you want to make it slightly less tedious. especially if you're not writing a lot JS it's not worth it to complicate the build for people by writing higher level JS

lilactown23:04:28

thanks, I’ll try and take another look at it soon

lilactown23:04:08

yeah it’s very minimal - the only reason I did it was to try and get better interop with native classes

lilactown23:04:26

trying to create classes the ES5 way doesn’t quite have the same behavior when the browser actually has native classes, so my hope was that I could rely on GCC to compile down the helper fn to either ES5 or actually use real classes depending on what the user wants to emit

lilactown23:04:24

this way from my cljs code I can just spew:

(helix.impl.class/createComponent
 React.Component
 #js {:constructor (fn [this] (set! (.-state this) #js {:foo "bar"}))
      :render (fn [this] ,,,)}
 nil)

dnolen23:04:52

@lilactown deps.cljs should be able to help you here too, but this feature is not under heavy usage - I haven't had a need myself - so I can't say more than we test basic stuff and that appears to work

lilactown23:04:40

gotcha. I’ll try and play with it over the weekend.

lilactown23:04:34

while I have your attention about this, would you ever consider a patch that added a form that emitted a class ? 😅

dnolen23:04:54

it's worth thinking about since that is a well known pain point - the problem is that you would need to transpile the generated JS again with Closure if we're going to maintain the nice fact that ClojureScript only generates ES3

dnolen23:04:41

people keep talking about generating higher level JS but I really see only tradeoffs and most of them not in favor

dnolen23:04:40

@lilactown https://clojurescript.org/guides/javascript-modules, the beginning of this explains what you need to do

👍 4
dnolen23:04:20

:foreign-libs [{:file "helix/impl/class.js" :module-type :es6}] should theoretically work for you in your deps.cljs

niwinz22:04:53

I have reported issue for this some time ago https://clojure.atlassian.net/browse/CLJS-2399 and seems not resolved

dnolen23:04:23

if it doesn't - bug

lilactown23:04:37

yeah, a lot of higher level features have a relatively minimal impact on applications, but a considerable impact on project maintenance

lilactown23:04:29

from the perspective of a maintainer of CLJS, I mean

dnolen23:04:57

right, higher level features mean a more complicated pipeline, worse perf, etc.

dnolen23:04:25

(compiler perf, possibly runtime perf - ES3 is fast)

lilactown23:04:15

yep. tho it was interesting to see what people were talking about re: future async/await perf benefits but that’s even a whole other level of effort, since AFAICT it would require so much work to make the way that CLJS handles things like do work with the function-boundary behavior of async/await

lilactown23:04:24

class is one that really bites me since more and more browsers are supporting it natively, and so interoping with them becomes more of a pain point. it’s minimal in most application code, but the places you need it you need it and you can’t get away with an ES5 mock of a class

dnolen23:04:38

people keep talking about async/await perf but honestly I find such talk meh esp. in browser context

dnolen23:04:12

Node.js really isn't that interesting for server IMO if you're already doing Clojure anyway

dnolen23:04:43

so that's why not much has happened here - I just don't see much value

dnolen23:04:26

I think some work on codegen for core.async is all that it really needs - it's not that slow

dnolen23:04:11

@lilactown re: class I'm not against this if it's selective

dnolen23:04:34

i.e. if you use defclass we'll generate ES6 and do a second pass w/ Closure on that file

dnolen23:04:04

it doesn't sound like a particularly hard project (I already have a Closure transpile in ClojureScript) - if you want to take a look at this be my guest

dnolen23:04:16

it's definitely an interop gaping hole

lilactown23:04:38

I’ll create a ticket and try and find time to take a look

dnolen23:04:24

in the ticket mention we need 2 parts - defclass macro and compiler support

👍 4
lilactown23:04:37

it’s not an immediate need - my Closure JS workaround seems to work OK - but it would be really nice to delete that code from my projects

dnolen23:04:10

sure, but is something people have asked for years

dnolen23:04:41

now that we have easy single file transpile support I don't really see any issues

dnolen23:04:13

it does affect source mapping but that can be done as separate enhancement ticket