This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-11-08
Channels
- # announcements (42)
- # aws (2)
- # babashka (69)
- # beginners (38)
- # calva (18)
- # cider (39)
- # circleci (1)
- # clj-commons (10)
- # cljs-dev (2)
- # clojure (36)
- # clojure-australia (14)
- # clojure-europe (25)
- # clojure-gamedev (40)
- # clojure-losangeles (4)
- # clojure-nl (5)
- # clojure-sweden (1)
- # clojure-uk (5)
- # clojurescript (133)
- # core-logic (24)
- # cursive (7)
- # datalevin (4)
- # datascript (3)
- # figwheel-main (1)
- # fulcro (45)
- # honeysql (1)
- # integrant (43)
- # introduce-yourself (1)
- # jobs (4)
- # leiningen (3)
- # lsp (32)
- # nextjournal (9)
- # pathom (18)
- # polylith (21)
- # portal (65)
- # re-frame (6)
- # releases (1)
- # remote-jobs (1)
- # reveal (12)
- # rewrite-clj (1)
- # sci (84)
- # tools-deps (22)
Is there a valid reason fixtures with test-vars
only work after you execute test-all-vars
or is this a bug?
$ plk
ClojureScript 1.10.597
cljs.user=> (require '[clojure.test :as t])
nil
cljs.user=> (t/deftest foo [])
#'cljs.user/foo
cljs.user=> (t/use-fixtures :each {:before (fn [] (prn :before))})
#'cljs.user/cljs-test-each-fixtures
cljs.user=> (t/test-vars [#'foo])
nil
cljs.user=> (t/test-all-vars 'cljs.user)
:before
nil
cljs.user=> (t/test-vars [#'foo])
:before
nil
@dnolen How do you mean? Like, in the package.json?
@dnolen I suspect it has to do with macro vs fn. macros have the opportunity to invoke the CLJS analyzer whereas a fn doesn't
@borkdude I don't know - I looked quickly - might just be a bug - test-vars
doesn't install the fixtures the way tets-all-vars
does
TMK I believe thheller was referring to the packages inside of the @firebase
node_module. So, there is no second package you can add to the package.json, it’s just firebase and then that comes with all the modules.
This is where firebase is kind of odd. You install firebase
(via npm or yarn) and it comes with all the sub packages e.g.
node_modules
...
@firebase/
auth/
app/
firestore/
auth-compat/
...
I noticed that yes. But test-vars
is a function. test-all-vars
is a macro which calls another macro which uses the CLJs analyzer,.
https://github.com/clojure/clojurescript/blob/6443518850d8e4f1e09be99d4669e8d4a5e893a3/src/main/cljs/cljs/test.cljc#L343
ah yeah, could maybe put a macro inside test-vars
to run the boilerplate side-effects?
yes! 🙏 (much better articulation than me)
or is that you cannot require @firebase/auth
from JS fundamentally - because it doesn't exist?
You can require @firebase/auth
and it will return the ESM module. So it’s found.
The issue seems to be that some of the the deps of @firebase/auth
are not resolved. One of those deps exxecute some side effects. Because those side-effects never execute, we get an error.
@tkjone but the thing is did you try this in a non-ClojureScript context i.e. does it fail in exactly the same way?
i.e. CommonJS require of this package, process Webpack/Rollup - it should fail in the same way
(though hard to see what - because all this stuff is intentionally pushed outside of ClojureScript)
> RE: did you try this in a non-ClojureScript context Yes. the demo repo is here https://github.com/athomasoriginal/firebase-cljs/tree/firebase-method-4
Is the src
dir you will see a deps.js
and index.js
file which should be setup in the same way, just with JS.
does that align with what you were thinking?
@tkjone you have no description of what happens in this case, or the others in the README
One moment. I will update. But for now: the issue is that when you call getAuth
it will throw an error in the console saying Error: Component auth has not been registered yet
well, it’s broken because of the use of require("@firebase/auth")
instead of the firebase recommended require('firebase/auth')
@tkjone https://www.npmjs.com/package/firebase why isn't this one scoped? can you not use the unscoped one?
How do you mean? TMK I am using the uncscoped package? Or do you mean use the require the unscoped package from CLJS?
haha yeah
Same. I am stumped how to google this in general, because it seems so “unique”.
I think that the firebase
package has dependencies on the namespaced @firebase/...
packages
which is why you get all of the @firebase/..
packages installed when you run npm i firebase
. theyre transitive
@tkjone have you tried importing the un-namespaced npm package, i.e. firebase/auth
no @
?
Yeah, npm install firebase/auth
will fail.
in CLJS? yes. That throws in CLJS
here's what I did:
$ mkdir firebase-test && cd firebase-test
$ npm init
$ npm i firebase
$ ls node_modules/firebase/auth
cordova dist package.json react-native
so firebase/auth
exists on disk. the description of the bug makes me think that the issue is that it doesn't exist on disk
firebase/auth
is a subdirectory of the firebase
package. is that the real issue here?
@lilactown that might be it - we only index the top level
but indexing more than the top-level is potentially problematic because of Node.js dep graphs - which is why we don't do it
it's a pretty common thing in npm packages these days to have one big package
and then instruct people to import some package/dir/subdir
@lilactown but how is this lower dir resolved?
not sure what you mean by "it is not relative" but, the way I understand it is:
import { baz } from 'package/foo/bar'
will:
1. look up package
in node_modules
2. search for the foo
directory in node_modules/package
. there might be some misdirection from package.json that I don't understand here but IME it just finds a directory in the package
3. search for bar
inside the foo
directory@lilactown you missed my point above
yes AFAICT there are slightly different resolutions between scoped packages and non-scoped packages and what's on disk
scoped:
node_modules/
@firebase/
package/
package.json
non-scoped:
node_modules/
firebase/
package/
...
package.json
I'm not sure what the solution is because I still don't know what the problem is yet. why can't @tkjone use:
(:require ["firebase/auth" :as auth])
?I suspect you know this happens, but when I try that I get a Compile Exception
No such namespace: firebase/app, could not locate firebase_SLASH_app.cljs, firebase_SLASH_app.cljc, or JavaScript source providing "firebase/app" (Please check that namespaces with dashes use underscores in the ClojureScript file name) in file src/demo/firebase_webpack.cljs
here's what I would expect that to resolve to on disk:
in my example instructions above:
$ cat node_modules/firebase/auth/dist/index.esm.js
export * from '@firebase/auth';
//# sourceMappingURL=index.esm.js.map
(at least wrt. resolution, it fails because of other reasons and that can be avoided w/ firebase/auth
except that doesn't work because ClojureScript didn't index it)
so it sounds like from that description, we do not index any subdirectories of packages
sorry that's what I was trying to say here https://clojurians.slack.com/archives/C03S1L9DN/p1636390721001100
ok, yeah so the problem might be that we just don't index firebase
as multiple packages
@tkjone I think this is just a resolution issue - allowing you specify something is not desirable
Thanks, @dnolen!
@tkjone have to set this aside for now but I reproduced the issue exactly https://github.com/clojure/clojurescript/pull/110
looking at the index dump - it's clear that firebase is malformed - I don't think this is a particularly hard issue - but need to make sure it doesn't affect what's already there
as to why it was reported before I suspect that firebase may have restructured the library
for re-frame apps, is there a difference in performance characteristics between these two approaches?
parent component/
├─ child component/
│ ├─ grandchild component/
│ │ ├─ @(rf/subscribe [:stuff-grandchild-needs])
│ ├─ child content
├─ parent-content
parent component/
├─ prop = @(rf/subscribe [:stuff-grandchild-needs])
├─ child component [prop]/
│ ├─ grandchild component [prop]/
│ │ ├─ [:div prop]
│ ├─ child content
├─ parent-content
assume each component is its own function, not inline. in plain english, does what level you put a subscription in the component hierarchy make a performance difference? having subscriptions littered throughout the component hierarchy makes devcards difficult without mocking rf/subscribe
for all of its calls throughout the component treeNot in and of itself, no. What matters is how many times everything has to render + size of each component (do they perform a lot of logic, or have a lot of HTML etc). This is why, you could have a nested structure and it can run quickly as long as things don’t need to update frequently. Ultimately, the only way to know for sure is you have to test everything.
My rule of thumb is I don’t like to work with deeply nested components because they become tricky to maintain. So, I start by optimizing for ease of development, then I work towards improving render performance if needed.
when react (& therefore reagent/re-frame) re-renders a component, it will re-render everything below it, too. what that means is it will do some equality checks to see if its props have changed, and if they have it will re-run the function to produce new VDOM nodes. all of this can take time
you can tune the performance of your application by moving subscriptions to different levels. subscriptions at the leaves tend to cause less calculations during render. however, your subscriptions can also be expensive - and depending on the shape and performance of your subscription graph, you might find that having a top-level subscription actually provides more performance than many subscriptions in the leaves
at the end of the day, you should profile your application to see where your performance problems lay
Agree with everything lilactown said 🙇
some frontend languages/frameworks (e.g. elm) are “smart enough” to discern that even though the arguments to a given function (i.e. props for a component) may change, the only DOM changes are at the leaves where the prop is actually being used in rendering, so re-render is limited to that grandchild leaf node dependent, which to me is the biggest performance concern because rerenders also affects app behavior (you lose hover states when a component rerenders, for example). is a re-frame/reagent webapp in that class of frameworks/libraries that can discern the smallest possible DOM update needed, or does it naively rerender the whole component tree when a prop changes?
I have never seen it happen for the reasons you described.
reagent/re-frame use React. so you get all the behaviors & benefits of React, plus some other stuff
When I have seen it happen? When I am absolutely obliterating the front end with chart data, user input, async calls etc. I suppose i’m saying I would have to try to get the effect your talking about 😉 But we’re talking in hypotheticals right now so it’s tough to cover all the fun scenarios.
React maintains a virtual representation of what’s currently on the page (called a virtual DOM, VDOM for short) and when your components “render,” what they’re actually doing is creating a new representation of this VDOM. React then diffs the new and old VDOM and determines what changes to make to the real DOM.
When React diffs the VDOM and then changes the DOM, they call this “committing” the latest VDOM
this makes it so that if a piece DOM hasn’t actually changed, you can maintain DOM state like hover or whatever like you said
and we can write our components in the sort of immediate-mode style as if they are going to change everything
so there’s this diffing and committing phase which can help save us some. however, this can be expensive, running all of the component functions to render the new VDOM and then diffing it. so React (& Reagent) also provide the ability to bail out of rendering by memoizing components, so that if they receive the same props in the same place in the tree, it will just reuse the last subtree rather than running the function for that component again and creating a new VDOM.
Reagent memoizes all of your components by default by comparing all props you pass in using Clojure equality =
. this does a deep equality check of nested structures like maps, vectors, etc.
sometimes this deep equality check can take too much time, at which point you want to start moving when and where new data is generated.
very often, it’s fine to just naively generate new VDOM on every change, and then start optimizing. React gives you all the tools you need here. reagent makes it slightly easier at the expense of slightly more complexity
@dnolen I think I might have identified this issue a while ago https://gist.github.com/athomasoriginal/fd00691bde0be8d9a52c33f3ca968bdc - the reason, as you noted, the scoped solution worked is because version 8 of the library was structured differently from the current version 9. Also, as I see now, the way I was looking at the problem was wrong.
@tkjone yes if firebase had been failing this whole time we would have heard about it long ago
haha indeed
in F# there is a method of accessing server logic from the client called https://github.com/Zaid-Ajaj/Fable.Remoting. What's your preferred method in clj/cljs?
The closest to that approach may be this: https://github.com/ptaoussanis/sente