Fork me on GitHub

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?


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

@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

Thank you for the explanations.


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


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


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


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


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


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


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.


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


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.


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


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.


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


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


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


all code will be converted like node_modules

Alex H20:04:01

I shall try that now


but that has issues with interop between JS->CLJS


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

Alex H20:04:49

ah. I currently need interop both ways


yeah thats a problem


problem is that shadow-js is not converted by closure


so closure doesn't know what parts you use


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


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


but that will be named xC or so


if you still do :simple that is fine

Alex H20:04:36

well, with :simple even the other thing works


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


how do you import?

Alex H20:04:49

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


hmm but that should be mangled all over the place?


or did you ^:export that?

Alex H20:04:50

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


ah ok yeah that can work to some extent then


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


so in the case of react components that should be fine


but you are playing with fire there


I wonder if the name mangling issues could be fixed


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


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


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


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

Alex H20:04:10

can do, yea


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


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


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


but that sucks writing by hand

Alex H20:04:29

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


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


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


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


shouldn't affect anything


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


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


maybe in there is a clue somewhere


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




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


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


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": [


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


only the plugins

Alex H20:04:00

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


ah ok. already had it extracted from the transit 😉


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


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


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


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


you can turn off name mangling completely


but that also turns it off for CLJS

Alex H20:04:10

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


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


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?


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


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


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


and add them to the externs

Alex H20:04:08

sounds... crazy. but might just work


well it is used today


just maybe filtering too much


will see if it extract onlyIn for example


hmm what the heck


yes it extract all of them already


{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]}


something must be missing


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


they should be in there

Alex H20:04:19

let me see


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;


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


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


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


hahaha doh!

Alex H20:04:11

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


yes but that is it


the pass only runs for npm code


not the classpath js


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?


yes but that just collects it from the JS


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


but thats slightly more complex due to the caching stuff


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


yeah if you feed everything to closure that will work


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


that will use it for everything


but its currently very unreliable


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) :


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


yeah .. like that


it is more reliable when only feeding it ES6


but barely any packages on npm provide that properly


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?


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


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


feeding it through :advanced destroys everything


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


did you restart after lein install?


and wipe .shadow-cljs/builds just in case


cache should have been wiped but you never know

Alex H21:04:34

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


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


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


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


yeah I'm stupid

Alex H21:04:32

at this point, I highly doubt that 😉


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


hmm something is not right


you can just comment out that line for a test

Alex H21:04:33

yea, on it


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


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


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!


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


@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.