Fork me on GitHub
#shadow-cljs
<
2018-04-21
>
achikin00:04:22

Actually reagent has React as cljsjs dependency. And if you download and compile the sample project above - you'll see that it pulls React from cljsjs. The question is - can I use :exclusions like in leiningen project setup to exclude that dependency?

justinlee04:04:52

I don’t think you have to (but you can). The cljsjs dependency won’t actually import the JavaScript because shadow’s cljsjs shim doesn’t let it. That’s why you need it in the package.json.

👍 4
thheller07:04:12

@achikin it downloads cljsjs packages since it just follows the dependencies. It does not however ever use any cljsjs packaged code. Its just on the classpath, never in your build. You can exclude it from the deps if you care about that but it doesn't matter.

👍 4
achikin13:04:49

Thank you for the explanations.

achikin13:04:22

I see that I can have :dev and :release configuration options. But can I have dev dependencies? Or it does not make sense?

thheller13:04:15

@achikin doesn't really apply to CLJS since the build decides what is used not the classpath

thheller13:04:43

there is one exception currently which re-frame-10x does with some stubs

thheller13:04:00

if you need that you currently must use lein or deps.edn

thheller13:04:20

I'll try to talk them out of that approach when I find some time

thheller13:04:02

but in general I find the pattern of using the classpath to replace certain namespaces horrible and annoying to work with

mynomoto14:04:57

I talked to Daniel about re-frame-10x and read the tracing code and the conclusion is that there is no harm in not using the stubs. You need only to have the correct closure defines for dev and prod. The tracing is added using macros that use the closure defines to decide to do the tracing or use the core fn and defn.

justinlee14:04:25

does the presence of warnings prevent hot swapping? i was doing a refactor and i have a bunch of warnings that i’m trying to fix and it seems like hot swap is off

justinlee14:04:10

At least I’m not losing it. Can I force a reload anyway or turn that feature off? Sometimes it is helpful to be able to iterate on code to get rid of the warnings.

thheller14:04:59

not currently but I can add if you like. open a ticket so I don't forget.

justinlee15:04:27

Great I’ll open a ticket when back to compy

Alex H17:04:51

@thheller I keep running into all sorts of half-mangled stuff with the advanced optimisation and the JS-on-classpath stuff; some things in the node_modules that get imported by that JS don't get mangled, yet in that JS the same property name does get mangled

Alex H17:04:03

leading to all sorts of obscure bugs

Alex H17:04:21

(and the check command doesn't show any of them)

Alex H17:04:32

not sure if you have any suggestions, but in the meantime I've reverted back to simple optimisations; it's a bummer because I lose DCE, but at least things work 😛

Alex H19:04:00

@thheller on a somewhat unrelated topic, how can I build a local test version from source of shadow-cljs? I managed to build it following the circleci setup, but it always tries to pull down the jar from maven/clojars, at least when I try and integrate that test version into the actual project I'm trying things on

Alex H19:04:13

I've worked out something terribly hacky by copying the jar into the local ~/.m2 under a new version that doesn't exist, etc, but I hope there's a better way 😛

Alex H19:04:04

I've tried hacking shadow-cljs so that it runs the js-on-classpath through closure/convert-sources-simple instead of closure/convert-sources, but perhaps unsurprisingly that doesn't really work, it doesn't recognize the es6 module export/imports

Alex H19:04:31

ah, I see how that was misguided.

thheller20:04:35

@alex340 do you have reproducable examples for the name mangling sometimes not working? seems like something the closure folks may be interested in

thheller20:04:52

if you mess with shadow-cljs just run lein install to install it locally

Alex H20:04:20

I've been messing with options to closure/convert-sources

Alex H20:04:43

trying to disable optimizations, for example, but that doesn't fix anything; it just reduces the bundle size somewhat

Alex H20:04:06

I don't have any nice factored-out examples of the mess I'm getting myself into with that name mangling, no. I might try that tomorrow

thheller20:04:35

to (if false ... so that the other branch is always chosen

thheller20:04:50

all code will be converted like node_modules

Alex H20:04:01

I shall try that now

thheller20:04:13

but that has issues with interop between JS->CLJS

thheller20:04:20

CLJS can use the JS just fine but not the other way

Alex H20:04:49

ah. I currently need interop both ways

thheller20:04:06

yeah thats a problem

thheller20:04:00

problem is that shadow-js is not converted by closure

thheller20:04:23

so closure doesn't know what parts you use

thheller20:04:28

so it will rename everything

Alex H20:04:30

well, it did compile with that, but I guess it won't actually work?

Alex H20:04:51

and the mangling issues are gone

thheller20:04:53

yeah because your .js files will still call cljs.core or whatever

thheller20:04:06

but that will be named xC or so

thheller20:04:25

if you still do :simple that is fine

Alex H20:04:36

well, with :simple even the other thing works

thheller20:04:50

so you don't call the CLJS from the JS?

Alex H20:04:13

I do. I import some react components from the CLJS into JS

thheller20:04:32

how do you import?

Alex H20:04:49

import { Toolbar as Toolbar_ } from "goog:dlt.components.editor.toolbar";

thheller20:04:24

hmm but that should be mangled all over the place?

thheller20:04:30

or did you ^:export that?

Alex H20:04:50

(def ^:export Toolbar (r/reactify-component toolbar))

thheller20:04:06

ah ok yeah that can work to some extent then

thheller20:04:22

but only if you call functions on it that are exported (or in externs)

thheller20:04:30

so in the case of react components that should be fine

thheller20:04:00

but you are playing with fire there

thheller20:04:24

I wonder if the name mangling issues could be fixed

thheller20:04:50

I only did very basic tests with all of this and never had this problem

thheller20:04:04

but you have taken this much farther than I ever did so far

Alex H20:04:55

the hack with treating them the same as npm fails in practice, even though it compiles

Alex H20:04:13

it falls over on one of those imported react components

thheller20:04:59

yeah like I said .. playing with fire ... surprised it worked for the Toolbar thing but I guess export saved it

Alex H20:04:08

anyway. maybe the way forward is to see if closure compiler on its own on that js file, without any of shadow-cljs or any cljs in general around it, trips up

Alex H20:04:53

and, if so, it should at least be possible to report it upstream

thheller20:04:58

can you share the JS code that gets mangled? maybe there is a clue in there somewhere

Alex H20:04:10

can do, yea

thheller20:04:59

might also be some of my externs inference or other JS processing. maybe its just skipping over something important

Alex H20:04:59

not exactly a piece of art.

Alex H20:04:16

the latest thing that was tripping it up is mangling the "onlyIn" to "zG" or whatever, but not in the soft-break module

thheller20:04:21

is it always the React.createElement props getting mangled?

Alex H20:04:35

no, I mean that "onlyIn" thing is not on a react element

Alex H20:04:46

it sometimes is props, sometimes other object properties

thheller20:04:12

well to be safe from mangling you could use 'onlyIn'

thheller20:04:24

but that sucks writing by hand

Alex H20:04:29

I know, and I've done that in a few places

thheller20:04:35

no idea why would rename some things and not others

Alex H20:04:37

but it's hard to find all the problems in the first place

Alex H20:04:15

I'd be happy to do that (or via externs) if shadow-cljs check printed those issues

thheller20:04:16

should be quick to find with shadow-cljs release app --pseudo-names or --debug

thheller20:04:45

I really don't understand why it doesn't

Alex H20:04:12

never used closure compiler on its own, but I think I'm going to see if I can set it up on just that one file (removing the cljs dependencies)

Alex H20:04:20

letting it loose on just the js + npm bits

thheller20:04:52

shouldn't affect anything

thheller20:04:07

CLJS it understands perfectly find and the npm bits it doesn't see

Alex H20:04:14

yea, I mean to get a nicer reproducer

thheller20:04:48

I wonder what the JS it generates for these files look like

thheller20:04:52

maybe in there is a clue somewhere

thheller20:04:46

can you compile (not release) and add the converted form to the gist?

thheller20:04:21

<output-dir>/cljs-runtime/module$whatever$name$it$had.js

thheller20:04:11

or .shadow-cljs/builds/<build-id>/release/closure-js/file/name.js

thheller20:04:29

thats transit but it contains the compiled code as well

Alex H20:04:56

I've added the transit file

Alex H20:04:01

let me see if I can also add the other one

thheller20:04:10

hmm one other thing. you aren't actually writing this code by hand? it looks like it was pre-processed by babel?

Alex H20:04:24

yep, it has a bit of babel preprocessing to be able to use React

Alex H20:04:45

This is the babelrc:

{
  "presets": [
    ["env", {
      "modules": false
    }]
  ],
  "plugins": [
    "transform-object-rest-spread",
    "transform-class-properties",
    "transform-react-jsx"
  ]
}

thheller20:04:11

maybe some of the babel stuff is confusing it. should not need the env preset

thheller20:04:17

only the plugins

Alex H20:04:00

the gist now also contains the file from the output dir/cljs-runtime

thheller20:04:37

ah ok. already had it extracted from the transit 😉

thheller20:04:26

so I'm guessing that the check stuff just does not check any objects

thheller20:04:52

some props don't get renamed since they are standard props with externs in either react or just DOM/HTML generic stuff

thheller20:04:02

one thing you could do is add /** @nocollapse */ {onlyLn:[...]}

thheller20:04:21

or rather write a babel plugin to add it for all JSX calls

thheller20:04:48

you can turn off name mangling completely

thheller20:04:55

but that also turns it off for CLJS

Alex H20:04:10

I thought you couldn't do that without also turning off DCE?

thheller20:04:57

you can fine tune almost everything but yeah disabling one thing might prevent another thing from working

thheller20:04:49

I could do something crazy

Alex H20:04:00

is there some sane way of getting closure compiler (standalone) to pick up the stuff in node_modules?

thheller20:04:32

the idea was to collect all properties assigned to module.exports = or exports.foo

thheller20:04:50

turn out to be pretty unreliable due do some of the dynamic stuff JS people do

Alex H20:04:58

sounds like you are suggesting adding externs for all properties that that thing finds

thheller20:04:08

it could however be used to collect every object property in those JS files

thheller20:04:14

and add them to the externs

Alex H20:04:08

sounds... crazy. but might just work

thheller20:04:38

well it is used today

thheller20:04:43

just maybe filtering too much

thheller20:04:58

will see if it extract onlyIn for example

thheller20:04:33

hmm what the heck

thheller20:04:39

yes it extract all of them already

thheller20:04:53

{tmp/alex.js=[hasMark, data, handleListOpClick, plugins, focus, type, hasInline, collapseTo, renderNode, ref, fontFamily, leaves, typeCell, checked, text, href, state, fontWeight, editorDeserialize, showLinkModal, onChange, textAlign, constructor, alt, textDecoration, renderMark, readOnly, check, fontStyle, handleBlock, node, borderRadius, style, object, tableOp, onKeyDown, flexGrow, blockName, typeItem, handleHideLinkModal, document, editorStateFromText, className, typeDefault, title, renderEditor, writable, spellCheck, listOp, flex, enumerable, handleLinkButtonClick, handleTableOpClick, disabled, onlyIn, hasBlock, placeholder, value, key, __proto__, editor, padding, types, backgroundColor, editorSerialize, typeTable, src, typeRow, handleMark, flexDirection, display, onPaste, marks, renderToolbar, toggleMark, prototype, handleHeading, editorEmptyState, nodes, toggleBlock, decorateNode, nodeKey, Editor, configurable]}

thheller20:04:20

something must be missing

thheller20:04:47

can you check if you .shadow-cljs/builds/<build-id>/release/externs.shadow.js has them?

thheller20:04:53

they should be in there

Alex H20:04:19

let me see

thheller20:04:32

if they aren't there might be a bug and they just get lost

Alex H20:04:44

❯ cat .shadow-cljs/builds/prod/release/externs.shadow.js | grep onlyIn ShadowJS.prototype.onlyIn;

thheller20:04:10

completely forgot that I already do the "crazy idea" I just had

thheller20:04:27

I started by only adding export.foo props but that didn't work well enough so I added everything

thheller20:04:35

but that seems to have limits as well

Alex H20:04:47

wait, so I just did another npx shadow-cljs release prod

Alex H20:04:56

and now that's not in there

thheller20:04:09

hahaha doh!

Alex H20:04:11

that might have been from before's experiment still with that if to treat it as npm

thheller20:04:22

yes but that is it

thheller20:04:26

the pass only runs for npm code

thheller20:04:36

not the classpath js

thheller20:04:55

when I just add it for classpath js it might just work

Alex H20:04:22

(.addCustomPass CustomPassExecutionTime/AFTER_OPTIMIZATION_LOOP property-collector)

Alex H20:04:23

I presume?

thheller20:04:40

yes but that just collects it from the JS

thheller20:04:40

they need to end up in the :js-properties set in the compiler state

thheller20:04:56

but thats slightly more complex due to the caching stuff

thheller20:04:09

let me see if I can code this together quickly

Alex H21:04:18

not sure if my example is really representative, but closure compiler does the right thing on a super trimmed down version with just that slate-soft-break, at least if I use the Es6 module that slate-soft-break provides

Alex H21:04:30

ah, no, it's not es6. never mind

Alex H21:04:50

but it certainly did rename the onlyIn in both places consistently

thheller21:04:38

yeah if you feed everything to closure that will work

thheller21:04:09

you can try :js-options {:js-provider :closure}

thheller21:04:17

that will use it for everything

thheller21:04:24

but its currently very unreliable

thheller21:04:34

and doesn't use ES6 code from node_modules

Alex H21:04:28

yea, that blows up

Alex H21:04:42

[2018-04-21 22:14:11 - SEVERE] node_modules/hoist-non-react-statics/index.js:7: ERROR - The define function must be called as a top-level statement. typeof define === 'function' && define.amd ? define(factory) :

thheller21:04:51

I know that it doesn't work for a whole bunch of stuff on npm

thheller21:04:13

yeah .. like that

thheller21:04:23

it is more reliable when only feeding it ES6

thheller21:04:34

but barely any packages on npm provide that properly

thheller21:04:52

so I have the propery-collector hooked up

Alex H21:04:33

somehow I'm still puzzled why this stuff doesn't just work (TM); don't you end up feeding everything to closure compiler as well? sure, the node_modules stuff gets pre-processed through babel (& a first round of closure compiler?) but then it gets fed in to build it all together, doesn't it?

thheller21:04:48

no. the :shadow-js stuff (ie. node_modules) is never passed to the :advanced compile

thheller21:04:07

it is processed separately with :simple and then added to the final build AFTER :advanced

thheller21:04:19

feeding it through :advanced destroys everything

thheller21:04:08

wasn't a whole lot of code but should do it

Alex H21:04:34

well, unless I'm useless, I don't think that worked

Alex H21:04:45

that said, it is a distinct possibility that I am useless

thheller21:04:53

did you restart after lein install?

thheller21:04:11

and wipe .shadow-cljs/builds just in case

thheller21:04:27

cache should have been wiped but you never know

Alex H21:04:34

didn't have the server running, so that's not it

thheller21:04:47

check if externs file includes it now

Alex H21:04:49

trying now after removing the .shadow-cljs dir wholesale

Alex H21:04:58

it didn't on the previous attempt just now

thheller21:04:26

just you running lein install should invalidate all cache since the shadow-cljs jar changed

thheller21:04:37

so that probably doesn't do anything but you never know 😉

Alex H21:04:09

well, I've added a println just above your changes for extra paranoia, and that's certainly getting printed

Alex H21:04:07

yea, definitely not working. not getting into that externs file, and not affecting what closure compiler outputs

thheller21:04:19

yeah I'm stupid

Alex H21:04:32

at this point, I highly doubt that 😉

thheller21:04:07

it is still only collecting those for :shadow-js 😉

thheller21:04:20

hmm something is not right

thheller21:04:28

you can just comment out that line for a test

Alex H21:04:33

yea, on it

thheller21:04:59

wonder why I'm generating the externs that way and not just from :js-properties

thheller21:04:08

the externs inference stuff uses that

Alex H21:04:30

it's in the externs now

Alex H21:04:44

and the output looks healthier indeed

thheller21:04:53

but does it run? 🙂

Alex H21:04:33

yep, that seems to work

Alex H21:04:43

it runs and that particular feature isn't broken anymore

Alex H21:04:51

thanks for your help, yet again!

thheller21:04:30

cool. I'll change the externs gen code to just use js-properties instead

thheller22:04:11

@alex340 cleaned up master a bit. please try it when you get a chance. will create a proper release tomorrow. too tired to test this properly now.