Fork me on GitHub
#shadow-cljs
<
2020-09-22
>
benny02:09:11

what’s the recommended way to include vendor css with shadow?

thheller03:09:09

whatever you prefer using to handle css. shadow-cljs provides nothing css related.

benny03:09:31

but with it being a build tool, is there a recommended approach to pulling in those files to the scope of the build?

benny03:09:15

even something like tailwind would require a build tool to move it from node_modules to public or am i thinking about this all wrong?

thheller03:09:20

there are far too many options that cover this. I have a rather basic bash script that does the copying or in even older projects I just copied things once myself and never touched it again

thheller03:09:43

you can also use something like ant, gulp, make, ....

hlolli11:09:35

If you use emotion, it would get bundled with the js assets. 0 configuration there.

thheller03:09:44

I use node-sass for older sass projects and postcss for tailwind projects

steveb8n05:09:44

Q: when building nodejs tests, I generally use the shadow auto-run feature. When (is (= foo bar)) fails, and foo is a (nested) map, the expected vs actual are quite difficult to read in the output. Is there a better technique (some kind of pprint or diff) for this?

steveb8n07:09:10

What I do today is pprint both values and eyeball them. I’m about to try adding a diff printing lib to this but was wondering if anyone else has any other tricks?

Eugen10:09:39

That would be great, I've also noticed test runs are hard to follow. I've added a junit xml export - but tha is not for console.

Eugen10:09:05

btw, do you have an example on how to use node fs api in a test? I'm trying to load some test resource files and I'm failing. The reports above are for browser tests with karma

steveb8n10:09:49

1. [“fs” :as fs]

steveb8n10:09:13

2.

(when-not (fs/existsSync out-dir)
  (fs/mkdirSync out-dir))

steveb8n10:09:27

3. (fs/readFileSync “test-resources/images/hobbit-house-original.jpg”)

steveb8n10:09:36

hopefully that gets you moving 🙂

steveb8n10:09:44

re: test output diffs. there are quite a few options these days. all of them good…

Eugen10:09:00

thanks, it worked ( I had something similar but it failed to work before)

steveb8n10:09:27

1. rebl 2. reveal 3. shadown inspect

steveb8n10:09:38

but none of them provide a nice diff out of the box

steveb8n10:09:43

glad it worked for you

👍 3
Eugen10:09:02

comming from a java background, regarding test vizualization in cljs, the setup I have now is very simplistic and not very usefull. I've started with clojure/clojurescript recently.

Eugen10:09:31

I know it's not diff related, but is this usefull: https://github.com/usrz/javascript-karma-verbose-reporter ?

thheller12:09:50

@U0510KXTU diff is actually kinda hard to do properly. I want to eventually add it to inspect but that will likely take a while.

steveb8n22:09:44

thanks everyone. I’ll try and use https://github.com/lambdaisland/deep-diff2 for now. that should do the job

steveb8n02:09:01

I’ve started using deep diff and the diff fn works well but it’s pretty-print fn does not display well in auto-run output. just providing the info for anyone that sees this

grounded_sage15:09:59

how does one troubleshoot the ChromeHeadless browser not launching when running karma start --single-run for the tests?

kenny15:09:12

shadow-cljs 2.11.4 seems to continuously crash my Chrome tabs. I don't have a minimal repro yet but perhaps this is a known issue already. We have two http servers running. One is for our main application and it runs on port 3000 (is is configured at [:builds :app :devtools :http-port]). The other is for our devcards setup. It's running on port 3001 and is configured via [:dev-http 3001]. The 3001 server has two :roots ["resources-devcards/public" "resources/public"] and has the push-state/index set to "devcards.html". To repro the problem, I start shadow-cljs up fresh. I navigate to http://localhost:3001/devcards.html and make a change to my css file. shadow-cljs will reload the css. In the console, I see "shadow-cljs: load CSS /css/main.css" printed exactly one time for each css change. This is the expected behavior. Now I open a second tab with my main app at http://localhost:3000/. I now make a single CSS change and see the "load CSS" message printed 3 times in the main app's console. If I make another change, I get the the load CSS message printed 6 times in the main app console. The next single change results in 12 load CSS. The whole time, the devtools console at 3001 is also printing out exponentially more load CSS messages. I also notice that even when I'm not making any changes to the CSS, the head element is continuously flashing in the Elements tab. This indicates that the head element is changing frequently. This pattern will continue until both tabs crash completely. The Chrome message is "Error code: SIGILL". This issue is quite disruptive to our workflow. I'm on Ubuntu 20.04 but we have other devs on Mac that have been experiencing this as well. Chrome 85.0.4183.102. This has popped up fairly recently so rolling back to an earlier version may be a good solution for the time being.

kenny15:09:29

The devcards.html file relies on two css files:

<link rel="stylesheet" type="text/css" media="screen,projection" href="/css/main.css">
    <link rel="stylesheet" type="text/css" media="screen,projection" href="/css/devcards.css">
And the main :app index.html file (which is also on the classpath of the devcards app) relies on just one file:
<link rel="stylesheet" type="text/css" media="screen,projection" href="/css/main.css">

thheller18:09:38

@kenny I cannot reproduce this. it is working completely fine and as expected for me

kenny18:09:20

I have not tried to create minimal repro but it is definitely happening continuously and is a new behavior.

kenny18:09:17

To be completely clear, as long as only one build is open (3000 or 3001), it will always work as expected.

kenny18:09:17

CLJS reloads do not appear to be affected. Seems to be CSS related only.

kenny18:09:49

Problem is present in 2.10.22 and not in 2.9.10. Have not tested anything in-between.

kenny18:09:21

In 2.9.10, I get 2 "load CSS" message printed in each tab for each CSS file change I make. It does not get exponentially larger though.

kenny18:09:55

Okay, I have gone through many versions now 🙂 From my findings, it appears the issue was introduced in v2.10.22. 2.10.0 - no issue, 2 load css msgs on change 2.10.6 - no issue, 2 load css msgs on change 2.10.16 - no issue, 2 load css msgs on change 2.10.21 - no issue, 2 load css msgs on change 2.10.22 - 2^(number of css changes) load css msgs printed on change 2.11.4 - same as 2.10.22

thheller19:09:13

hmm that is indeed the version where the logic for this changed

thheller19:09:23

but not a clue how it would get to that behavior

kenny21:09:51

No idea either. Behavior is very odd. Would you like me to file an issue for this?

kenny22:09:08

Would be interesting to try a patched version of shadow-cljs that reverts that change to see if that was really it.

kenny22:09:33

Stared at it for a few mins and couldn't think of any reason why. Don't know if relevant but cloneNode will copy inline listeners.

thheller22:09:14

hmm that might be relevant but then this should always be a problem?

kenny22:09:30

Presumably. Perhaps most people don't set inline listeners on their links.

kenny22:09:34

Still don't have an explanation for why the copying of inline listeners would exhibit this behavior. I suspect it would require deeper knowledge than just this function.

kenny22:09:30

Primarily wrt the problem only occurring when more than 1 tab is open.

thheller22:09:28

do you have other stuff going on in the page? maybe browser extensions?

kenny22:09:00

Definitely have some extensions. Can in an incognito browser.

kenny22:09:49

Same issue in an incognito browser.

kenny22:09:06

Another thing to note is if I have two tabs both pointed to localhost:3000, I do not have the issue. It only occurs when I have 1 tab of localhost:3000 and 1 tab of localhost:3001

kenny22:09:07

Same thing for two tabs with localhost:3001.

kenny22:09:33

The moment I open a tab on a different port than the one I started on, every CSS change from that point forward will have the issue.

kenny15:09:13

fyi, created an issue here that includes all of the Slack info: https://github.com/thheller/shadow-cljs/issues/789

kenny18:09:54

Some very odd behavior when I watch the head element in the Elements tab in Chrome. After making a CSS change, I observe 2^(number of css changes) <link> elements added. It will then slowly start removing some number of those. This is with 2.10.22.

Pavel Klavík18:09:17

Hi, trying to import DayjsUtils from @date-io/dayjs and it does not want to work. Here is the link to its code: https://github.com/dmtrKovalenko/date-io/blob/master/packages/dayjs/src/dayjs-utils.ts#L62 and here is my code:

(:require ["@date-io/dayjs" :default DayjsUtils]
            ["@material-ui/pickers" :refer (DatePicker MuiPickersUtilsProvider)]
            [orgpad.client.util.date :as date])
I have also tested other versions of including this, the value of DayjsUtils in REPL is nil. I was following https://material-ui-pickers.dev/getting-started/usage

neilyio18:09:53

What error are you getting?

Pavel Klavík19:09:03

DayjsUtils is nil

Pavel Klavík19:09:17

the rest of code looks like this:

[:> MuiPickersUtilsProvider {:utils DayjsUtils}
   [:> DatePicker {:value     (date/parse "2020-06-18" true)
                   :on-change #(js/console.log %)}]]

Pavel Klavík19:09:18

react_devtools_backend.js:2273 Warning: Failed prop type: The prop `utils` is marked as required in `MuiPickersUtilsProvider`, but its value is `undefined`.
    in MuiPickersUtilsProvider (created by orgpad.client.views.widgets.date_picker.date_picker)
    in orgpad.client.views.widgets.date_picker.date_picker (created by orgpad.client.views.widgets.grid.item)
    in div (created by ForwardRef)
    in ForwardRef (created by WithStyles(ForwardRef))
    in WithStyles(ForwardRef) (created by orgpad.client.views.widgets.grid.item)
    in orgpad.client.views.widgets.grid.item (created by orgpad.client.views.widgets.grid.grid)
    in div (created by ForwardRef)
    in ForwardRef (created by WithStyles(ForwardRef))
    in WithStyles(ForwardRef) (created by orgpad.client.views.widgets.grid.grid)
    in orgpad.client.views.widgets.grid.grid (created by orgpad.client.views.widgets.paper.paper)
    in div (created by ForwardRef)
    in ForwardRef (created by WithStyles(ForwardRef))
    in WithStyles(ForwardRef) (created by orgpad.client.views.widgets.paper.paper)
    in orgpad.client.views.widgets.paper.paper (created by orgpad.client.views.administration.users.user_filter)
    in orgpad.client.views.administration.users.user_filter (created by orgpad.client.views.widgets.grid.item)
    in div (created by ForwardRef)
    in ForwardRef (created by WithStyles(ForwardRef))
    in WithStyles(ForwardRef) (created by orgpad.client.views.widgets.grid.item)
    in orgpad.client.views.widgets.grid.item (created by orgpad.client.views.widgets.grid.grid)
    in div (created by ForwardRef)
    in ForwardRef (created by WithStyles(ForwardRef))
    in WithStyles(ForwardRef) (created by orgpad.client.views.widgets.grid.grid)
    in orgpad.client.views.widgets.grid.grid (created by orgpad.client.views.administration.users.users)
    in orgpad.client.views.administration.users.users (created by function orgpad$client$views$administration$core$administration(){
return reagent.core.create_class(new cljs.core.PersistentArrayMap(null, 4, [new cljs.core.Keyword(null,"component-did-mount","component-did-mount",-1126910518),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"component-did-update","component-did-update",-1468549173),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"display-name","display-name",694513143),orgpad.client.views.administration.core.administration,new cljs.core.Keyword(null,"reagent-render","reagent-render",-985383853),orgpad.client.views.administration.core.render_administration], null));
})
    in div (created by function orgpad$client$views$administration$core$administration(){
return reagent.core.create_class(new cljs.core.PersistentArrayMap(null, 4, [new cljs.core.Keyword(null,"component-did-mount","component-did-mount",-1126910518),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"component-did-update","component-did-update",-1468549173),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"display-name","display-name",694513143),orgpad.client.views.administration.core.administration,new cljs.core.Keyword(null,"reagent-render","reagent-render",-985383853),orgpad.client.views.administration.core.render_administration], null));
})
    in div (created by function orgpad$client$views$administration$core$administration(){
return reagent.core.create_class(new cljs.core.PersistentArrayMap(null, 4, [new cljs.core.Keyword(null,"component-did-mount","component-did-mount",-1126910518),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"component-did-update","component-did-update",-1468549173),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"display-name","display-name",694513143),orgpad.client.views.administration.core.administration,new cljs.core.Keyword(null,"reagent-render","reagent-render",-985383853),orgpad.client.views.administration.core.render_administration], null));
})
    in div (created by function orgpad$client$views$administration$core$administration(){
return reagent.core.create_class(new cljs.core.PersistentArrayMap(null, 4, [new cljs.core.Keyword(null,"component-did-mount","component-did-mount",-1126910518),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"component-did-update","component-did-update",-1468549173),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"display-name","display-name",694513143),orgpad.client.views.administration.core.administration,new cljs.core.Keyword(null,"reagent-render","reagent-render",-985383853),orgpad.client.views.administration.core.render_administration], null));
})
    in function orgpad$client$views$administration$core$administration(){
return reagent.core.create_class(new cljs.core.PersistentArrayMap(null, 4, [new cljs.core.Keyword(null,"component-did-mount","component-did-mount",-1126910518),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"component-did-update","component-did-update",-1468549173),orgpad.client.views.administration.core.load_administration_data,new cljs.core.Keyword(null,"display-name","display-name",694513143),orgpad.client.views.administration.core.administration,new cljs.core.Keyword(null,"reagent-render","reagent-render",-985383853),orgpad.client.views.administration.core.render_administration], null));
} (created by orgpad.client.views.administration.core.administration)
    in orgpad.client.views.administration.core.administration (created by reagent4)
    in reagent4 (created by orgpad.client.views.widgets.lazy.lazy)
    in Suspense (created by orgpad.client.views.widgets.lazy.lazy)
    in orgpad.client.views.widgets.lazy.lazy (created by root-component)
    in div (created by root-component)
    in div (created by root-component)
    in root-component (created by orgpad.client.views.root.root)
    in orgpad.client.views.root.root
overrideMethod @ react_devtools_backend.js:2273
printWarning @ checkPropTypes.js:21
checkPropTypes @ checkPropTypes.js:83
validatePropTypes @ react.development.js:1715
createElementWithValidation @ react.development.js:1814
reagent$impl$template$make_element @ template.cljs:422
reagent$impl$template$native_element @ template.cljs:334
reagent$impl$template$vec_to_elem @ template.cljs:366
reagent$impl$template$as_element @ template.cljs:385
reagent$impl$component$wrap_render @ component.cljs:114
reagent$impl$component$do_render @ component.cljs:141
(anonymous) @ component.cljs:164
reagent$ratom$in_context @ ratom.cljs:42
reagent$ratom$deref_capture @ ratom.cljs:55
reagent$ratom$run_in_reaction @ ratom.cljs:537
reagent$impl$component$render @ component.cljs:164
finishClassComponent @ react-dom.development.js:17161
updateClassComponent @ react-dom.development.js:17111
beginWork @ react-dom.development.js:18621
beginWork$1 @ react-dom.development.js:23180
performUnitOfWork @ react-dom.development.js:22155
workLoopSync @ react-dom.development.js:22131
performSyncWorkOnRoot @ react-dom.development.js:21757
(anonymous) @ react-dom.development.js:11090
exports.unstable_runWithPriority @ scheduler.development.js:654
runWithPriority$1 @ react-dom.development.js:11040
flushSyncCallbackQueueImpl @ react-dom.development.js:11085
flushWork @ scheduler.development.js:598
channel.port1.onmessage @ scheduler.development.js:165
material-ui-pickers.js:48 Uncaught TypeError: Utils is not a constructor
    at eval (material-ui-pickers.js:48)
    at mountMemo (react-dom.development.js:15443)
    at Object.useMemo (react-dom.development.js:15739)
    at Object.exports.useMemo (react.development.js:1522)
    at MuiPickersUtilsProvider (material-ui-pickers.js:47)
    at renderWithHooks (react-dom.development.js:14804)
    at beginWork (react-dom.development.js:17483)
    at HTMLUnknownElement.callCallback (react-dom.development.js:189)
    at Object.invokeGuardedCallbackImpl (react-dom.development.js:238)
    at invokeGuardedCallback (react-dom.development.js:293)

Pavel Klavík19:09:27

Ok, so requiring it like this fixed the problem:

(ns orgpad.client.views.widgets.date-picker
  (:require ["@date-io/dayjs" :as dayjs]
            ["@material-ui/pickers" :refer (DatePicker MuiPickersUtilsProvider)]
            [orgpad.client.util.date :as date]))

(defn date-picker
  []
  [:> MuiPickersUtilsProvider {:utils dayjs}
   [:> DatePicker {:value     (date/parse "2020-06-18" true)
                   :on-change #(js/console.log %)}]])
Fuck this shit, I don't think I will ever understand enough JS to know how this stuff works.

neilyio19:09:37

I just tried it out and got this same thing, it imports as nil.

neilyio19:09:27

Glad you sorted it out! I know it's confusing, I've been studying ClojureScript/shadow-cljs for three months and I'm just barely starting to get productive.

Pavel Klavík19:09:42

Clojure(Script) itself is great, I am rarely strugling with anything, mostly I can just concentrate on my business problems, but including JS libraries is sometimes tricky and much more annoying. The hardest part is that you will get example code in JS which you have to translate into CLJS and it is often try and guess for me, plus dealing with mutability is annoying 🙂

Pavel Klavík19:09:30

But the language is superb compared to anything else, due to REPL, hot code reloading, simplicity, data orientation, ... I would never change it for anything.

neilyio18:09:55

I'm trying to get the https://www.11ty.dev static site generator to work with shadow-cljs. I expect Eleventy to read my shadow-cljs .js output files and produce HTML files. Eleventy does its thing with a command line utility, so I've got a build hook setup with shadow-cljs to call node node_modules/.bin/eleventy on the :flush event. Eleventy needs a module.exports = my_render_func in the JS module that it reads, so I'm using the :esm target in my shadow-cljs.edn. As Eleventy expects CommonJS, I'm using the NodeJS esm library to make my shadow-cljs output compatible. This means that in my :flush build hook, I'm calling (sh "node" "-r" "esm" "node_modules/.bin/eleventy"). So far, this is working perfectly with shadow-cljs compile site. Eleventy produces all the expected HTML files. The problem comes when I run shadow-cljs watch site. Shadow-cljs gets stuck at the "compiling..." stage and never finishes. Do you have any suggestions for a workaround? Eleventy seems to be a good fit so far for shadow-cljs, and I feel like I'm really close to a smooth workflow. I'd love to write up a blog post to help others with shadow-cljs + Eleventy, but this part has me stumped.

neilyio18:09:40

@thheller I hope you can give me some guidance! Here's my build config, as well as the :flush hook.

neilyio18:09:56

:builds
 {:site
  {:target :esm
   :output-dir ".shadow-cljs-cache"
   :build-hooks
   [(build.hooks/eleventy)]
   :modules {:index.11ty {:exports
                          {default pages.index/default}}}}}}

neilyio18:09:08

(defn eleventy
  {:shadow.build/stage :flush}
  [build-state & args]
  (let [output (sh "node" "-r" "esm" "node_modules/.bin/eleventy")]
    (when-not (empty? (:err output))
      (println (:err output)))
    (println (:out output)))
  build-state)

thheller19:09:45

@neil.hansen.31 not a clue. looks fine but I don't have a clue what eleventy even is or does. does it not have a watch command itself that you can run separately and skip the hook altogether?

neilyio20:09:10

@thheller thanks for the response. I know it's tough to answer questions like this about an external tool... Eleventy does have its own watch command, but there's a few details that prevent it from working well with shadow-cljs compile.

neilyio20:09:49

Right now my build is producing index.11ty.js, which exports.default a function that calls reagent's render-to-static-markup and returns an HTML string. Eleventy is basically running index.11ty.js, taking the HTML string, and creating the resulting HTML file (with some templating that it takes care of).

neilyio20:09:22

I diff'd two versions of index.11ty.js, one created by shadow-cljs compile and one created by shadow-cljs watch. I noticed that there's a bunch of extra shadow.* namespaces that are imported in the version created by shadow-cljs watch, I figure some of these for hot-reloading functionality in the browser and providing the ability to connect to a REPL. I suspect that when Eleventy evaluates index.11ty.js, it's getting stuck somewhere on this REPL/hot-reload code.

thheller20:09:55

if you don't need hot-reload or REPL you can set :devtools {:enabled false} in your build config

thheller20:09:15

why are you not using :node-library though?

thheller20:09:29

:esm seems like a weird choice if you actually want commonjs?

thheller20:09:54

this never directly runs in the browser right?

neilyio20:09:52

I tried :node-library a while, but I got the same error as in https://github.com/thheller/shadow-cljs/issues/304.

No protocol method ISwap.-swap! defined for type cljs.core...

neilyio20:09:11

And I don't seem to be able to do a module.exports = my_render_func with the :npm-module target, which is mentioned later in that issue.

thheller15:09:37

you could just create a foo.js yourself that just has module.exports = require("./path-to-npm-module-output/your.ns").the_export;

thheller15:09:52

with (defn ^:export the-export [foo bar] ...) in that ns

thheller15:09:26

but I still don't have a clue what you are doing ... so good luck with boot 😉

thheller20:09:16

how are you running into the issue you linked? do you have two builds loading in the script?

neilyio21:09:23

Wanted to address this question of yours. I'm running into this issue when I use Eleventy's --watch command, as you suggested in your first response. With this alternative workflow, I set Eleventy up to call shadow-cljs compile site every time it reloads.

neilyio21:09:53

It actually works the first time, as soon as I start Eleventy, but as soon as I make a change to a file, the reload errors with No protocol method ISwap.-swap! defined for type cljs.core/Atom: [object Object].

thheller20:09:37

I assume its just running node to do stuff and generate HTML?

thheller20:09:50

I don't know enough about eleventy so I can't really help and linked issues that you guess might be related but also might not be doesn't let me help you

thheller20:09:46

the issue that is immediately suspicious to me is the build hook

neilyio20:09:29

Which part?

neilyio20:09:57

Also, if this can be helpful at all, here's a GIF of what I'm seeing in the REPL on shadow-cljs watch. It's hanging on Compiling ..., and after I ctrl-c to exit, I'm getting a successful message Eleventy saying Copied 9 files / Wrote 1 file in 0.94 seconds, followed by shadow-cljs - #3 ready!. The HTML generation does in fact work perfectly after I ctrl-c to get out of the Compiling... message.

neilyio20:09:30

Update: I can confirm that adding {:devtools {:enabled false}} as you suggested fixes the hanging problem. It no longer gets stuck on Compiling.... I feel even closer to the solution now, but Hot Reload really seems important to the workflow. Fingers crossed you have an idea for me!

thheller22:09:56

I still don't have the slightest clue about what you are actually trying to do here sorry

thheller22:09:14

my assumption would be that you'd just have a regular generate HTML page and include the JS afterwards

thheller22:09:34

including the JS to build the thing in the first place is something entirely different and I've never done that using react (ie. SSR)