Fork me on GitHub
#shadow-cljs
<
2020-02-15
>
robert-stuttaford05:02:22

is it possible to change the red colour that shadow prints in a cider emacs buffer to something else?

p-himik15:02:32

It seems that source maps are not used by the browser if I create a release build with :compiler-options {:source-map true}. Meanwhile, they are used with watch. Did I not add some flag?

Ivan Koz15:02:29

If anybody using shadow-cljs with the Cursive, how do you connect to cljs watcher repl? Currently i'm calling (shadow.cljs.devtools.api/repl :app) manually, any better way to do it?

thheller15:02:21

@robert-stuttaford color should be disabled if you are connected over nrepl? otherwise you may (alter-var-root! shadow.build.warnings/*color* (constantly false)).

robert-stuttaford08:02:31

seems it's not the warnings, but the top-level status

robert-stuttaford08:02:05

i tried the alter-var-root, it had no effect

robert-stuttaford08:02:43

i'm using cider-connect-clj&cljs to connect to shadow's nrepl service

thheller08:02:46

uhm yeah that is not shadow-cljs making that red

thheller08:02:52

that is never red

robert-stuttaford09:02:39

ok, thanks! i'll dig in cider!

thheller15:02:43

@p-himik should work fine? what do you mean by "not used"?

p-himik16:02:16

If I use shadow-cljs watch and trigger an exception in the browser, the stacktrace shows correct CLJS files, and I can open them in the browser. If I use shadow-cljs release, the stacktrace shows minified files. Of course, I cannot use shadow-cljs built-in debug server for this (I think), so I use python -m http.server in the directory with index.html.

thheller16:02:06

all that matters is that the .map file is created and that the .js file has a //# sourceMappingUrl ... at the bottom

thheller16:02:35

beyond that I don't what would be different

p-himik16:02:45

Yes, the .map is there. And the sourceMappingUrl is there as well. In fact, the Python server shows that the source map is downloaded by the browser. But it's not used for some reason.

thheller16:02:27

in the chrome devtools sources tab

thheller16:02:34

it should show the original sources somewhere?

thheller16:02:05

maybe you are somewhere in the code it can't map. don't really know what you are doing

thheller16:02:24

normally source map coverage should be pretty good though

p-himik16:02:00

Yes, Chrome shows the CLJS sources there. I just throw an error when I click a button. With the release build, it results in this in the JS console:

Uncaught Error: Button clicked
    at main.js:593
    at Object.da (react-dom.production.min.js:17)
    at Oa (react-dom.production.min.js:18)
    at ub (react-dom.production.min.js:18)
    at wa (react-dom.production.min.js:18)
    at vb (react-dom.production.min.js:20)
    at O (react-dom.production.min.js:20)
    at P (react-dom.production.min.js:20)
    at Mi (react-dom.production.min.js:82)
    at ig (react-dom.production.min.js:289)
With the debug build, it results in the correct core.cljs file being mentioned with the correct line.

p-himik16:02:11

The app itself is the smallest one created from https://github.com/shadow-cljs/lein-template/tree/master/resources/leiningen/new/shadow_cljs that uses the latest version of shadow-cljs and updated React stuff.

p-himik17:02:05

And I'm pretty sure that the source map itself is just fine because: 1. There's no warning about it being malformed 2. I can see the original CLJS files in DevTools but I don't see any requests for them Do you have any idea why the source map is still not used for the stacktraces? Do you need anything from me to aid you?

thheller17:02:18

I really can't do anything if the trace is that deep in react

thheller17:02:51

in release the minified react version is used. react doesn't ship source maps so there is no possibility of mapping it back

p-himik17:02:42

But how would React affect my source code? I just removed React from the equation by just throwing an exception straight in the start function - same deal.

p-himik17:02:06

Notice how it says main.js in the exception printout.

thheller18:02:25

ah yeah I can explain that

thheller18:02:38

but that has nothing to do with your actual error ...

thheller18:02:47

I'm assuming you are calling the init via :init-fn

thheller18:02:53

that call itself has no source map

thheller18:02:09

and closure inlines it so init no longer exists and just directly throws at the top level

p-himik18:02:37

And yet again, it's not that. :(

<script>reagent_playground.core.init();</script>

p-himik18:02:53

It's still that example from your repo. :)

thheller19:02:54

I'm not sure what you are aftter

thheller19:02:06

the init-fn call NEVER has source maps

thheller19:02:21

and if init just calls start then closure will inline that so there no longer is a start

thheller19:02:59

and adding an exported function really doesn't matter there at all

p-himik19:02:21

I do not use init-fn.

p-himik19:02:19

That script tag above works. Meaning, the ^:export works. Meaning, the core/init function is not inlined anywhere.

p-himik19:02:08

Then how is it being called from the HTML file?

thheller19:02:19

you are misunderstanding how this works. look at the bottom of the JS file how exports are created

thheller19:02:31

basically it'll have inline the body of start into init

thheller19:02:40

and then bound the resulting funciton to the "exported" name

p-himik19:02:03

I have replaced (start) with just the exception. This is what I have in CLJS:

(defn ^:export init []
  (throw (js/Error. "Hi there")))
And this is what it ends up as in the compiled JS:
function Xf() {
    throw Error("Hi there");
}

thheller19:02:09

what exactly are you looking for? that react error from previously isn't anywhere near what you are looking at now

p-himik19:02:23

But that's the whole problem, there's nothing I haven't described. I have the above code. In the debug mode, DevTools matches it with the source map. In the release mode, it does not - that is the problem.

thheller19:02:48

no your example is too simplistic

thheller19:02:12

do something that actually survives advanced optimizations and isn't inlined

thheller19:02:04

also don't throw in init ...

thheller19:02:23

even a setTimeout helps

p-himik19:02:04

The exact same issue with setTimeout.

thheller20:02:41

do more stuff .. thats all I can say. add an event listener to body that after the fifth click throws or something

thheller20:02:58

something that actually involves running code that is NOT react and NOT in init

p-himik20:02:16

That worked. Well, to some extent - it said that I had an error somewhere deep in cljs.core. But that's definitely not the case.

p-himik20:02:06

The code:

(defn do-bad-stuff [stuff]
  (throw (js/Error. stuff)))

(defn ^:export init []
  (.setTimeout js/window #(do-bad-stuff "From setTimeout 0") 0)
  (.setTimeout js/window #(do-bad-stuff "From setTimeout 100") 100))

thheller20:02:04

but still inlined 😛

p-himik20:02:51

God dammit... :)

thheller20:02:30

easy way to escape inline is multi methods 😛

p-himik20:02:34

Or self-referencing, it seems.

p-himik20:02:52

So now I have this

(defn do-bad-stuff [delay]
  (.setTimeout js/window #(do-bad-stuff (+ delay 1000)) delay)
  (throw (js/Error. delay)))

(defn ^:export init []
  (do-bad-stuff 0))
compiled as this
var ca = function ca(a) {
    window.setTimeout(function () {
        var c = a + 1E3;
        return ca.a ? ca.a(c) : ca.call(null, c)
    }, a);
    throw Error(a);
};

function ia() {
    return ca(0)
}
and outputting this
Uncaught Error: 0
    at ca (main.js:4821)
    at Object.ia [as init] (main.js:4825)
    at (index):14
So it somehow became worse.

thheller20:02:36

uhm thats still throwing in init ...

p-himik20:02:13

The subsequent throws that happen asynchronously have the exact same message.

p-himik20:02:59

A new iteration:

(defn do-bad-stuff [delay]
  (.setTimeout js/window
               (fn []
                 (do-bad-stuff (+ delay 1000))
                 (throw (js/Error. delay)))
               delay))

(defn ^:export init []
  (do-bad-stuff 0))
var ca = function ca(a) {
    return window.setTimeout(function () {
        var c = a + 1E3;
        ca.a ? ca.a(c) : ca.call(null, c);
        throw Error(a);
    }, a)
};

function ia() {
    return ca(0)
}
Uncaught Error: 0
    at core.cljs:10374

p-himik20:02:21

So: - Not inlined - Not throwing in init - Still wrong location

thheller15:02:15

@nxtk that is currently the only way. you can create a custom REPL command and bind it to a key to make it "easier"

Ivan Koz15:02:37

good idea, ty

Ivan Koz16:02:04

can shadow-cljs refresh page, reconnect when connection was closed?

Wilson Velez18:02:33

Hi, I’m implementing a reframe app with highcharts.js, one the options the library has is to show a message when there are no data to draw, https://api.highcharts.com/highcharts/noData, but for it to works it need to include a js file, the file is inside the npm module (highcharts/modules/no-data-to-display.js), righ now I’m requiring

["highcharts/highstock" :as Highcharts]
["highcharts-react-official" :default HighchartsReact]
and it works fine, but without the no data label, how should I include this file?

Wilson Velez18:02:42

when I include the name as

["highcharts/module/no-data-to-display"]
I get > Compilation failed! > The required JS dependency “highcharts/module/no-data-to-display” is not available, it was required by “fund_performance/stockchart.cljs”. > > Search in: > /Users/wvelezva/dev/workspace/fund-performance/node_modules > You probably need to run: > npm install highcharts/module/no-data-to-display > > See: https://shadow-cljs.github.io/docs/UsersGuide.html#npm-install

p-himik18:02:13

You have a typo, it should be "modules".

Wilson Velez18:02:50

thanks, now I don’t have the message but it doesn’t show the no data message

Wilson Velez18:02:04

so, the way I’m including the file is fine?

p-himik18:02:15

Since it doesn't complain anymore, it should be fine, yes. > The actual text to display is set in the lang.noData option Did you set the option?

Wilson Velez18:02:21

this is all the configuration I’m using

{:credits {:enabled false}
   :title ""
   :rangeSelector {:buttonPosition {:x -200}} ;;to hide the zoom buttons but letting the input dates visible
   :legend {:enabled true}
   :lang {:noData "There are no data to display or there are any Fund Selected"}
   :noData {:style {:fontWeight "bold"
                    :fontSize "15px"
                    :color "#303030"}}
   :plotOptions {:series {:showInLegend true
                          :events {:click #(.log js/console (str "p= " (.. % -point -x) " sn=" (.. % -point -series -name)))}}}
   :tooltip {:pointFormat "{series.name}: <b>{point.y:.1f}%</b>"}
   :series [{:type "line"
             :name "Random data"
             :data []}]}

Wilson Velez18:02:36

:lang {:noData "There are no data to display or there are any Fund Selected"}

p-himik18:02:15

No idea. If you convert this structure to JS recursively (i.e. with something like clj->js), then it looks fine to me. Does your data work in a pure JS environment?

Wilson Velez15:02:19

Hi @p-himik, sorry for the delay (family calls) 🙂, and thanks for your help, I created this fiddle, locally it also works https://jsfiddle.net/ju5oqme3/

p-himik15:02:57

> locally it also works You mean, you got it working after all, so the case is closed?

Wilson Velez15:02:51

no, plain js works

p-himik15:02:04

Can you create a CLJS project on GitHub that has everything that you need and that looks just like that JS that works? So I could check it out and maybe see what's wrong.

Wilson Velez15:02:50

I don’t want to abuse your help, I hope this isn’t too much trouble for you

p-himik15:02:18

That's why I don't try to create the project myself. :)

p-himik15:02:28

But if you create it, I can look.

Wilson Velez16:02:29

sorry it take me too long, I’m not used to work with github

p-himik16:02:23

Don't sweat it, I'm not just sitting and waiting. :)

p-himik16:02:09

An advice about Git - don't push before you check the contents of the commits. If you seen something out of place (like committed compiler cache), just amend the required commit, maybe with interactive rebase. And only then push. This way, it won't end up in the history. Right now, for example, Git had to download almost 10k files because of this. But the actual source code is just a few.

Wilson Velez16:02:40

yes, I know, my bad

p-himik17:02:53

Got it working.

Wilson Velez17:02:00

what was my problem?

p-himik17:02:47

I figured it out from the source code of the no-data module, but turns out that there's also documentation for this: https://github.com/highcharts/highcharts#load-highcharts-as-a-commonjs-module

p-himik17:02:52

Notice how they load modules.

p-himik17:02:20

It's not just a require or import. The also call the imported function on the Highcharts object.

p-himik17:02:11

You just need the replace the no-data require with this: ["highcharts/modules/no-data-to-display" :as no-data-module] And call (no-data-module Highcharts) somewhere at the top level or in init.

Wilson Velez17:02:17

yes, I noticed that but it was complete “chinese” to me

Wilson Velez17:02:09

I really know very little about modules, commonjs and the others

Wilson Velez17:02:41

I didn’t know it was a function I thought it was some kind of import

p-himik17:02:30

Heh, yeah, the proliferation of the ways to require JS stuff in the JS world makes it a swamp I dare not to venture into. That's why most of the time I just look at the compiled output and try to understand what's going on, while also debugging it in the browser.

Wilson Velez17:02:46

I don’t know how to thank you!

p-himik17:02:21

No problem. :)

Wilson Velez17:02:47

so this line makes two things

require('highcharts/modules/exporting')(Highcharts)

Wilson Velez17:02:52

one is the require

Wilson Velez17:02:24

the other is call the “module” function over the Highchart object

Wilson Velez17:02:50

module being, exporting or no-display-to-data in my case