Fork me on GitHub
#shadow-cljs
<
2020-06-11
>
e19:06:57

reposting from #reagent as I'm wondering if this is a shadow-cljs issue: I have the following bit of code to create a datepicker:

(ns ui.components.picker.core
  (:require ["@material-ui/pickers" :refer [TimePicker DatePicker MuiPickersUtilsProvider]]
            ["@date-io/moment" :as MomentUtils]
            [ui.utils.core :as utils]
            [reagent.core :as reagent]))

(def component-styles {})
(def component-props {:inputVariant "outlined" :fullWidth true})

(defn date-picker-real [props & children]
  (let [all-props (-> component-props (merge props))]
    (utils/->component DatePicker all-props
                       :styles component-styles
                       :children children)))

;; Call this datepicker from the UI; really it's just wrapping the actual DatePicker component with the MuiPickersUtilsProvider, which is necessary to get this all to work.
(defn date-picker [props & children]
  [:> MuiPickersUtilsProvider {:utils (reagent/as-element MomentUtils)} [date-picker-real props children]])
This works well if I compile to a browser target. But when I compile to npm module, I get error messages like the following:
VM53713 react_devtools_backend.js:6 Warning: Failed prop type: Invalid prop `utils` of type `object` supplied to `MuiPickersUtilsProvider`, expected `function`.
    [. . .]
Uncaught TypeError: Utils is not a constructor
    at eval (useUtils-cfb96ac9.js?2ccd:11)
    at mountMemo (react-dom.development.js?61bb:15442)
    at Object.useMemo (react-dom.development.js?61bb:15738)
    at useMemo (react.development.js?72d0:1521)
    at MuiPickersUtilsProvider (useUtils-cfb96ac9.js?2ccd:10)
    at renderWithHooks (react-dom.development.js?61bb:14803)
    at mountIndeterminateComponent (react-dom.development.js?61bb:17482)
    at beginWork (react-dom.development.js?61bb:18596)
    at HTMLUnknownElement.callCallback (react-dom.development.js?61bb:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js?61bb:237)
Seems strange the behavior would change depending on compilation method, so wondering if I might be missing something?

lilactown19:06:04

the module resolution might change depending on the config

lilactown19:06:23

can you log what MomentUtils is in the npm build?

e19:06:21

@lilactown do you mean something like (js/console.log (reagent/as-element MomentUtils)) ?

lilactown19:06:48

remove the as-element… what is MomentUtils? we need to know

thheller19:06:18

@rbruehlman my guess would by the {:utils (reagent/as-element MomentUtils)} isn't correct and that it expects an actual class/function there. so maybe just {:utils MomentUtils}

e19:06:41

here's the object (somewhat truncated... not quite sure how to better format)

Module {__esModule: true, Symbol(Symbol.toStringTag): "Module", default: ƒ}
default: ƒ MomentUtils(_a)
arguments: (...)
caller: (...)
length: 1
name: "MomentUtils"
prototype:
addDays: ƒ (date, count)
date: ƒ (value)
endOfDay: ƒ (date)
endOfMonth: ƒ (date)
format: ƒ (date, formatString)
formatNumber: ƒ (numberToFormat)
getCalendarHeaderText: ƒ (date)
getDatePickerHeaderText: ƒ (date)
getDateTimePickerHeaderText: ƒ (date)
getDayText: ƒ (date)
getDiff: ƒ (date, comparing)
getHourText: ƒ (date, ampm)
getHours: ƒ (date)
getMeridiemText: ƒ (ampm)
getMinuteText: ƒ (date)
getMinutes: ƒ (date)
getMonth: ƒ (date)
getMonthArray: ƒ (date)
getMonthText: ƒ (date)
getNextMonth: ƒ (date)
getPreviousMonth: ƒ (date)
getSecondText: ƒ (date)
getSeconds: ƒ (date)
getWeekArray: ƒ (date)
getWeekdays: ƒ ()
getYear: ƒ (date)
getYearRange: ƒ (start, end)
getYearText: ƒ (date)
isAfter: ƒ (date, value)
isAfterDay: ƒ (date, value)
isAfterYear: ƒ (date, value)
isBefore: ƒ (date, value)
isBeforeDay: ƒ (date, value)
isBeforeYear: ƒ (date, value)
isEqual: ƒ (value, comparing)
isNull: ƒ (date)
isSameDay: ƒ (date, comparing)
isSameHour: ƒ (date, comparing)
isSameMonth: ƒ (date, comparing)
isSameYear: ƒ (date, comparing)
isValid: ƒ (value)
mergeDateAndTime: ƒ (date, time)
parse: ƒ (value, format)
setHours: ƒ (date, count)
setMinutes: ƒ (date, count)
setMonth: ƒ (date, count)
setSeconds: ƒ (date, count)
setYear: ƒ (date, year)
startOfDay: ƒ (date)
startOfMonth: ƒ (date)
constructor: ƒ MomentUtils(_a)
__proto__: Object
__proto__: ƒ ()
property: ƒ (prop, desc)
apply: ƒ apply()
arguments: (...)
bind: ƒ bind()
call: ƒ call()
caller: (...)
constructor: ƒ Function()
length: 0
name: ""
toString: ƒ toString()
Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()
get arguments: ƒ ()
set arguments: ƒ ()
get caller: ƒ ()
set caller: ƒ ()
__proto__: Object
[[FunctionLocation]]: <unknown>
[[Scopes]]: Scopes[0]
[[FunctionLocation]]: index.esm.js?d37f:4
[[Scopes]]: Scopes[4]
Symbol(Symbol.toStringTag): "Module"
__esModule: true
__proto__: Object

e19:06:01

@thheller gave that a shot too, and no dice

thheller19:06:16

maybe you just don't have the correct object? whats the JS example you are basing this on?

lilactown19:06:46

it looks like the npm-module build is resolving it as an ES module. note the "default" key in that object

thheller19:06:30

@lilactown thats incorrect. note the __esModule. that is es module rewritten by babel to commonjs

thheller19:06:17

but yeah maybe try ["@date-io/moment" :default MomentUtils]

lilactown19:06:53

@rbruehlman says that it works in browser build, so I have to assume that it resolves to the correct function somehow in that context

lilactown19:06:17

I’m also not convinced that creating an element out of it is correct, but just narrowing down on why you would see different behaviors between different build configs

e19:06:27

Right, yeah, it resolves in the browser build. I'll try :default and see if it gets me anywhere. I didn't write the code in question, but a JS example would be something to the effect of:

import MomentUtils from '@date-io/moment';
import DateFnsUtils from '@date-io/date-fns';
import LuxonUtils from '@date-io/luxon';
import { MuiPickersUtilsProvider } from 'material-ui-pickers';
 
function App() {
  return (
    <MuiPickersUtilsProvider utils={DateFnsUtils}>
      <Root />
    </MuiPickersUtilsProvider>
  );
}
 
render(<App />, document.querySelector('#app'));

e19:06:15

my coworker, who wrote the code, said he fought with it for a long while and was only able to get it to work in the browser with (reagent/as-element MomentUtils) in lieu of just MomentUtils , so hence the choice of as-element

e19:06:07

oh, hey, :default worked, with no need for as-element. Interesting quirk. Thanks for the help!

lilactown19:06:26

yeah, as-element would be equivalent to <MomentUtils /> in JS

thheller19:06:32

yeah the above is using the default export too

rschmukler20:06:18

Hey @thheller - re. your comment on my PR - would you be open to a PR that removes the semvar.js dep and instead uses a CLJ-based implementation?

thheller20:06:51

absolutely. as long as it parses node versions in the same way. no extra dependency.

thheller20:06:01

that lib doesn't seem to handle version ranges no uses everywhere?

rschmukler20:06:42

Yeah, I think you're right - just pulled up the shadow code to see what you were using it for now

rschmukler20:06:04

Looks like we would likely need a re-write

rschmukler20:06:34

Still, I might take a crack at porting it over since I'm playing with loom for my runtime and GraalVM + Loom looks like a long way out

thheller20:06:58

I mean its just a nashorn replacement. doesn't have anything else to do with graal vm

thheller20:06:01

just the regular JDK

rschmukler20:06:51

Definitely not sure - a bit lacking in my knowledge of Graal nuance. My understanding was that Graal was a special cut of the JVM - I take it that it's not?

thheller20:06:34

there is graalvm yes. completely separate and not related to the graaljs scriptengine

thheller20:06:43

well related yes but not required

thheller20:06:00

I'm can't possibly force everyone to use graalvm 😛

rschmukler20:06:23

Haha, indeed, I was wondering how you were going to address it in non-nashorn releases

thheller20:06:58

its just a regular JVM dependency, no need to switch your entire jvm

rschmukler20:06:38

I will have to look at graaljs a bit more to understand where the boundaries are. Seeing as that there are other JS deps in shadow anyway, porting to graaljs probably makes more sense since it seems unlikely to get off of JS completely, right?

thheller21:06:58

no other changes required most likely

thheller21:06:18

but yeah some JS support is still required for the babel stuff

thheller21:06:31

don't really want to go back to node for that but could

rschmukler21:06:42

Yes! I was saying that that seems like a better alternative than rewriting semver.js in cljc

rschmukler21:06:01

(ie. porting semver.js to cljc)

thheller21:06:34

well I'd still prefer a CLJ impl for that part. bit of a gigantic overhead to launch an entire JS vm for a bit of version parsing

thheller21:06:59

just can't be bothered to figure out all the crazy node version strings out there 😛

rschmukler21:06:15

I might take a poke at using graaljs since getting that into the project seems like a long-term goal anyway, and it could serve as an easier fix to porting

rschmukler21:06:24

If that's something that you'd still be interested in

thheller21:06:05

there is really not much to do .. its mostly about testing in a few different JDK versions and so on

rschmukler21:06:23

Playing with it now 🙂

thheller21:06:32

why are you trying to use loom with shadow-cljs though?

rschmukler21:06:44

Hahaha, it's indeed a bit wonky

rschmukler21:06:54

The long and short of it is that I have cider starting shadow and it seems to use shadow to manage both the clj JVM and the cljs process

rschmukler21:06:53

(and I could be 100% wrong about what cider-jack-in-clj&cljs is actually doing, but as far as I can tell it's starting a JVM and then loading nREPL on the backend, and then starting shadow as java lib, and then starting a second nREPL on the JS side)

thheller21:06:39

no clue what cider does 😛

rschmukler21:06:47

That makes two of us 😉

dpsutton21:06:07

can i recommend using cider-jack-in-clj and cider-jack-in-cljs separately?

dpsutton21:06:26

the one you are using does expect that the same jvm can juggle both sessions. not usually true with shadow

dpsutton21:06:53

but if you check your *Messages* buffer it will say the one command being used to start a jvm

rschmukler21:06:39

Even if I did use it separately, wouldn't it use the same default java command? ie. won't they be the same JVM runtime version unless I explicitly configure two different runtime paths ?

rschmukler21:06:56

(right now 15-loom is my "default" JDK)

dpsutton21:06:03

presumably one will use lein/deps/boot and one will use shadow

dpsutton21:06:17

same jvm runtime version and default java command but that seems neither here nor there

rschmukler21:06:24

But shadow (the node package) invokes java on the $PATH right?

rschmukler21:06:45

I might be wrong, but then I would expect that it'd just be two separate processes both running loom anyway - which I think leaves me in the same pickle of needing a nathorn replacement in the JDK-15 based build

thheller21:06:21

are you using loom as your default JVM? that seems ... brave? 😛

rschmukler21:06:36

Borderline insane I'd say

rschmukler21:06:53

But yeah, I can't resist playing with the concurrency constructs

thheller21:06:02

just use JAVA_HOME=~/install/loom clj -m your.loom-stuff?

rschmukler21:06:20

or nix right? 😉

thheller21:06:45

never used nix before but there are plenty of options to select a different JDK per project 😛

rschmukler21:06:52

But none the less, it seems like shadow will need to overcome nathorn disappearing at some point, regardless of how crazy bleeding edge I am

thheller21:06:27

yes. everything is in place. like I said .. add the dependency and change "nashorn" to "JavaScript". that should be all there is to it.

thheller21:06:15

I just need to get around testing it a bit more .. which I'll do when jdk15 is actually out and nashorn actually removed.

rschmukler21:06:18

Playing with it thus far - looks like there may be additional work to get it to register with the ScriptManager but not convinced that I'm doing it right yet

thheller21:06:04

did you add these ttwo?

rschmukler21:06:10

Well there's my problem 😛

rschmukler21:06:35

Didn't see the second dep - testing now

thheller21:06:50

didn't I also read somewhere that nashorn will be available as regular dep?

thheller21:06:15

just like the removed a bunch of stuff after jdk8 and turned it into libs instead? like jaxb?

thheller21:06:28

didn't check up on it in a while

thheller21:06:05

doesn't really matter ... if all else fails I'll just remove the semver.js ... its annoying anyways

rschmukler21:06:20

I admittedly have almost no context here. That being said, it seems like graal-js might be the better pick anyway?

rschmukler21:06:37

That being said, I use loom, so you can tell that I favor cutting myself on the bleeding edge

rschmukler21:06:59

(ie. comparing whether to introduce the nathorn lib vs the graal-js lib)

thheller21:06:08

yeah I don't want to get too hyped about loom just yet .. still probably a couple years out right?

rschmukler21:06:21

JDK 18 I think is what I remember reading

thheller21:06:28

so 2 years ...

thheller21:06:02

+ another 10 till enough people have it to warrant actually using it in shadow-cljs 😛

rschmukler21:06:26

But it's definitely hype worthy. https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ is a fantastic read on why a runtime based scheduler is preferable

rschmukler21:06:03

Yeah I don't know if it'll ever matter in the context of shadow - and definitely a way out. But in terms of what it means for the JVM, quite exciting to see

thheller21:06:25

oh its absolutely game changing if it actually works 😛

thheller21:06:59

we'll see how the performance and stuff works out

rschmukler21:06:36

I'm curious - why don't you think it would work? Haven't Go and BEAM (Erlang), and to some extent GHC, demonstrated that a run-time based scheduler solution can work?

thheller21:06:55

well there is going to be a tradeoff somewhere. this can't be free. either overall performance, GC perf or whatever.

thheller21:06:32

erlang also has a quite different memory/process model which makes this a whole lot easier

rschmukler21:06:34

In some initial toy benchmarks I made 5000 HTTP requests simultaneously using both aleph (manifold based, nio) and hato (clj-http mimicing client wrapping JDK 11 http client). Loom was about 2x as fast

rschmukler21:06:10

I expect that that might have something to do with aleph's default thread pool as it didn't manage to "light up" the cores nearly as effectively as loom

thheller21:06:34

just the fact that you don't have to think about thread pools anymore is soo nice 😛

rschmukler21:06:41

Yeah, it's wonderful

rschmukler21:06:55

Also, seriously, not leaking deferred / promises all over

rschmukler21:06:04

(or go channels, whatever your concurrency primitive is)

thheller21:06:23

yeah no go macro anymore. just async/virtual-thread or whatever 🙂

rschmukler21:06:26

suddenly concurrency becomes caller controlled, not implementing function's responsibility

rschmukler21:06:53

I've had it in erlang and haskell, and it's 100% the thing I miss most

rschmukler21:06:21

Also, I imagine something like aleph's let-flow which could disect a body of code and automatically parallelize paths

rschmukler21:06:02

eg.

(parallelize
  (let [a (foo)
        b (bar)
        c (+ foo bar)]
   (println "Hello")
   c))

rschmukler21:06:44

Imagine something that disects the body to run (foo) (bar) and (println) in parallel, and then rejoins on c

thheller21:06:19

I'm happy with core.async if I can get rid of the go macro. just blocking ops everywhere. already using mostly async/thread in shadow-cljs and its sooo much better than go

rschmukler21:06:08

Yep! Yeah, you still need channels / streams to coordinate execution within. ie. for the same reason I prefer (map f coll) to imperative manipulation, I want ways to operate over queues (eg. stream/map) - but to not have to consider whether something inside those transformations is "blocking" is 😍

rschmukler21:06:54

Anyway, sorry for nerding out haha. Got the engine working. How should I best test this? JDK 8, 11, 13 and 15-loom sound like an okay checklist?

rschmukler21:06:14

(15-loom is for me, I'll admit it!)

thheller21:06:41

I mostly care about jdk8 tests

thheller21:06:08

if that works then all the newer ones should too

thheller21:06:04

not like the node world were everything breaks constantly 😛

rschmukler21:06:41

Awesome - I'll test it out on JDK 8 and make sure it works. Also going to add a couple tests (mostly just porting over your semver-intersects comments) just so there's something in case you ever wire up multi JDK CI tests

thheller21:06:07

I can add a system property or config entry so you can pick which js engine to use

thheller21:06:28

I'd rather delay switching away from nashorn. not fond of adding extra deps if I don't have to.

thheller21:06:37

already have too many

rschmukler22:06:26

Ah, okay. It does considerably cleanup the module to remove nashorn (eg. make-engine* and get-jvm-major-version can be removed)

rschmukler22:06:50

But, I tested on open-jdk-8 and all is good either way... So if you don't want that cleanup in the PR let me know

thheller22:06:52

not interested it making it the default just yet. still want the option to be available though.

thheller22:06:53

although whats another 20mb of deps right? 😛

rschmukler22:06:10

I mean, shadow isn't exactly light anyway. You're interoping with NPM, etc

rschmukler22:06:24

Maybe it's just me personally, but I don't expect it to be a lightweight library

thheller22:06:30

I actually think its fairly light compared to some projects I've seen 😛

rschmukler22:06:13

No comment there! I just meant given that it's close to an IDE in some respects, it gets some license in my mind

thheller22:06:14

still ... would like it to be smaller 😛 and start faster ...

rschmukler22:06:45

So, regarding the changes to the actual file then, if I'm not cleaning up detecting the major JVM version, etc. do you just want a one-liner string substitution + the deps changes + the tests?

rschmukler22:06:15

https://github.com/thheller/shadow-cljs/pull/731 If it's easier to collaborate there (through in the cleanup, but 100% happy to undo it)

thheller22:06:17

let me think about this for a bit. too tired to decide now. its either removing all the version check stuff and just using graal or some system property/config entry to control which engine is used

rschmukler22:06:46

Sounds good! I'll just wait for whatever you decide on the PR. Thanks again for all the help 🙂