shadow-cljs

https://github.com/thheller/shadow-cljs | https://github.com/sponsors/thheller | https://www.patreon.com/thheller
Karol Wójcik 2020-10-02T09:32:42.202200Z

Is it possible to include dependency only on nodejs? Something like (:require-node)?

thheller 2020-10-02T10:07:08.202400Z

only via https://shadow-cljs.github.io/docs/UsersGuide.html#_conditional_reading

❤️ 1
Aron 2020-10-02T10:59:18.203200Z

I am a bit confused with the missing runtime error. I have a node-test build that I ran with node, but that doesn't yield a runtime, or at least not one I could yet connect to.

jcf 2020-10-02T11:30:49.207400Z

Hello all! I'm working on a little prototype web app built on top of AWS Amplify, and I've hit an interesting problem when applying advanced optimisations. Amplify generates code for you that defines the attributes of models that can be found in your GraphQL backend. In my case the generated models/index.js file contains this:

// @ts-check
import { initSchema } from '@aws-amplify/datastore';
import { schema } from './schema';



const { Product, ListedProduct, List } = initSchema(schema);

export {
  Product,
  ListedProduct,
  List
};
The schema file is a JavaScript file with lots of data…
export const schema = {
    "models": {
        "Product": {
In development I can require these models from Clojurescript with (:require ["../models/index" :as model]) , and things like model/Product work just fine. In production, with advanced compilation, the value of model/Product is undefined. In fact, all three of the models I'm playing with are undefined.
(js/console.log "Models:" #js {:List          model/List
                                 :ListedProduct model/ListedProduct
                                 :Product       model/Product})
That logs this in Firefox:
Models: 
{…}

List: undefined

ListedProduct: undefined

Product: undefined

<prototype>: Object { … }
grocer.cljs:272:48

jcf 2020-10-02T11:31:24.207600Z

I'm fairly sure this is some dead code elimination/var renaming taking place during advance compilation.

jcf 2020-10-02T11:34:16.207800Z

I initially expected some property was being renamed, which is why Amplify wouldn't initialise the DataStore it provides, but given these vars resolve to nothing…

thheller 2020-10-02T12:29:13.208300Z

@ashnur node-test does not support a REPL. just use node-repl instead.

thheller 2020-10-02T12:30:55.208400Z

yeah closure will rename this code. you can either create externs or access the names by string

thheller 2020-10-02T12:31:49.208600Z

something like https://github.com/applied-science/js-interop j/get-in or so

thheller 2020-10-02T12:32:09.209200Z

is quick too though

thheller 2020-10-02T12:32:25.209400Z

could maybe even generate that automatically somehow

jcf 2020-10-02T12:32:38.209600Z

Wouldn't model/Product get renamed consistently though?

jcf 2020-10-02T12:33:52.209800Z

I'm using js-interop in places, and I looked at adding simple externs.

thheller 2020-10-02T12:33:53.210Z

no because the JS file uses "Product" (as in a String)

jcf 2020-10-02T12:34:57.210200Z

So I can't do something like this:

(ns foo
  (:require ["../models" :as models]))

(.subscribe DataStore models/Product)

jcf 2020-10-02T12:35:25.210800Z

I'd need to do (.subscribe DataStore (j/get-in models :Product))?

thheller 2020-10-02T12:35:51.211Z

with the simple externs you can do models/Product

jcf 2020-10-02T12:36:21.211200Z

In externs/<build>.txt I need to add something like this?

# Fix Amplify's models being undefined :)
models/Product

thheller 2020-10-02T12:37:12.211400Z

just Product

jcf 2020-10-02T12:37:35.211600Z

Awesome. I'll echo ... into a file and mint a release build now. 👍

jcf 2020-10-02T12:39:31.211800Z

@thheller I love you! That did it. 💥

jcf 2020-10-02T12:39:39.212Z

Thank you so much!

👍 1
Aron 2020-10-02T14:21:25.212600Z

there is no such target as node-repl

Aron 2020-10-02T14:21:38.212800Z

I am even more confused now 🙂

Aron 2020-10-02T14:25:04.213600Z

thanks, I found this, but I am not yet sure how to use it for the purpose I need the repl

dpsutton 2020-10-02T14:26:10.214Z

that will start a repl, right? then you can develop and require your code as desired

Aron 2020-10-02T14:27:18.214300Z

i wish it were as simple

Aron 2020-10-02T14:28:13.215200Z

I need to somehow connect it to my editor, right? because "no one actually types in the repl, who would do that" I saw this on twitter. And copy pasting lots of stuff is difficult anyway

dpsutton 2020-10-02T14:29:08.215700Z

yeah most likely. CIDER can do this. what's your editor?

Aron 2020-10-02T14:29:18.215900Z

neovim/conjure

dpsutton 2020-10-02T14:30:33.217600Z

i think if you put the nrepl options in the shadow config it will start up with that stuff and you should be able to connect as normal

Aron 2020-10-02T14:30:44.217900Z

I usually have to do a ConjureConnect to the nrepl port and then ConjureShadowSelect to select the build/namespace I am connecting to. But this second part doesn't work

Aron 2020-10-02T14:31:29.218600Z

If I write ConjureShadowSelect it tells me that the watch for the build is not running. Which is not true, it's running, but it's a node-test build.

Aron 2020-10-02T14:32:11.219400Z

So, just to be clear, I don't think either tool is wrong in any way, I just don't know what I don't know that would connect it.

thheller 2020-10-02T14:39:20.220100Z

@ashnur I don't have a clue about the conjure parts. shadow-cljs node-repl will launch it as will (shadow.cljs.devtools.api/node-repl) (from CLJ)

thheller 2020-10-02T14:39:41.220700Z

if ConjureShadowSelect asks for a build id that would be :node-repl (but it needs to be started elsewhere first

Aron 2020-10-02T14:41:29.221300Z

But what port?

thheller 2020-10-02T14:41:58.221500Z

the usual nrepl port

thheller 2020-10-02T14:42:53.222Z

maybe there is an option in conjure to start the node-repl directly. I don't know.

Aron 2020-10-02T14:44:50.222500Z

the usual is already used by the actual build of the application running

Aron 2020-10-02T14:46:12.223400Z

ok, I started the node repl, and tried to connect to 3334 port which seem to work, but if I try to evaluate the buffer, it just throws errors

thheller 2020-10-02T14:46:57.223900Z

you need to do the select thingy

thheller 2020-10-02T14:47:02.224100Z

otherwise you are just in a clojure REPL

Aron 2020-10-02T14:48:51.225Z

right, sorry, I did do that. I selected :node-repl as you said. The evaluation certainly works, I think I have a new problem now, not related to either conjure or shadow. Thanks

Olical 2020-10-02T15:09:09.225500Z

You may also want to use node-repl, not :node-repl when selecting

scottlowe 2020-10-02T16:21:33.230600Z

I'm using an js-joda via an npm module and my target is :browser. Unfortunately the js-joda code contains calls to the ES2016 feature .includes function (on Array) instead of indexOf which fails in IE11. I've tried all sorts of shadow-cljs config combinations wtihout any luck, e.g. :js-options {:babel-preset-config {:targets {:ie 11}} or :compiler-options {:rewrite-polyfills true} which I believe only writes polyfills for ES6+ anyway.

scottlowe 2020-10-02T16:21:34.230800Z

Has anybody managed to configure shadow-cljs such that an ES2016 feature is re-written or polyfilled for ES5 compatibility?

thheller 2020-10-02T16:22:48.231500Z

the output is supposed to be es5 assuming you didn't override :output-feature-set

thheller 2020-10-02T16:23:00.231900Z

but maybe it doesn't detect the .includes properly

dpsutton 2020-10-02T16:23:11.232200Z

https://shadow-cljs.github.io/docs/UsersGuide.html#_output_language_options (for reference)

thheller 2020-10-02T16:23:45.232600Z

there is :compiler-options {:force-library-injection #{"polyfill-name"}}

thheller 2020-10-02T16:23:50.232800Z

don't know the includes name though

thheller 2020-10-02T16:25:07.234300Z

or just add the polyfill via external lib like https://polyfill.io/v3/

scottlowe 2020-10-02T16:25:29.234800Z

Thanks, both of you. I believe I've tried the correct :output-feature-set and other config options already, but I'll double check. Just to confirm, I don't need the babel-preset-config at all?

scottlowe 2020-10-02T16:26:27.235800Z

I will probably fallback to adding the polyfill manually, I was just convinced that shadow-cljs would take care of it and found that idea attractive. Anyway, let me go away and check my config again. Perhaps some of the config options I've got in there are interfering with each other. Thanks

scottlowe 2020-10-02T16:32:17.237300Z

Hmm... no, I can't get it to work. Perhaps the includes call isn't being detected properly, as you say. For completeness, the offending lines in the third-party code are chained calls, so perhaps that makes it harder to detect https://github.com/js-joda/js-joda/blob/a1a635a7e4e506dfa7a9eedcc932571bdba9c1b2/packages/locale/src/format/cldr/CldrDateTimeTextProvider.js#L226

scottlowe 2020-10-02T16:32:36.237800Z

Anyway, I will simply include the polyfills via that :force-library-injection config key for now. Thanks!

scottlowe 2020-10-02T16:49:47.239Z

That worked 🙂 I also uncovered a call to Object.values. The final shadow-cljs config was :compiler-options {:force-library-injection ["es6/array/includes" "es6/object/values"]}

👍 1