This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-10-07
Channels
- # aleph (1)
- # announcements (2)
- # babashka (1)
- # beginners (49)
- # calva (1)
- # cider (5)
- # clj-kondo (14)
- # cljdoc (11)
- # cljsrn (6)
- # clojure (120)
- # clojure-austin (1)
- # clojure-europe (6)
- # clojure-finland (3)
- # clojure-italy (29)
- # clojure-nl (6)
- # clojure-spec (38)
- # clojure-uk (14)
- # clojurescript (65)
- # code-reviews (8)
- # cursive (20)
- # data-science (1)
- # datascript (5)
- # datomic (57)
- # emacs (6)
- # figwheel-main (2)
- # fulcro (32)
- # funcool (1)
- # jackdaw (7)
- # jobs (3)
- # joker (7)
- # kaocha (8)
- # keechma (3)
- # nrepl (7)
- # off-topic (25)
- # quil (3)
- # re-frame (10)
- # reagent (43)
- # remote-jobs (1)
- # ring (1)
- # shadow-cljs (173)
- # sim-testing (1)
- # spacemacs (1)
- # sql (3)
- # tools-deps (34)
Is there a way to specify a Clojure flag per build? So then my macros can see it at compile time?
similar to a JVM property, but per build
@currentoor that is generally not a good idea since those flags won't be accounted for in the caching logic
what kind of flag do you have in mind though? is that cross-platform macro or just a macro expanding CLJS code?
@thheller I’ve got macros and functions that need to emit different stuff depending on whether I’m targeting the browser or react native
#?(:cljs
(defsc ErrorBoundary [this {:keys [body-fn]}]
{:shouldComponentUpdate (fn [np ns] true)
:getDerivedStateFromError (fn [error]
{:error true
:cause error})
:componentDidCatch (fn [error info]
(log/error :componentDidCatch (ex-message error)))}
(let [{:keys [error]} (comp/get-state this)
error-msg (div :.ui.error.message
(div :.header "Error Encountered")
(dom/p "Please try again or contact support if error persists."))]
(if error
error-msg
(try
(body-fn)
(catch :default error
(log/error error :render-try-catch)
error-msg))))))
#?(:cljs (def ui-error-boundary (comp/factory ErrorBoundary)))
#?(:clj (defn error-boundary* [[first-arg :as body]]
`(let [real-parent# comp/*parent*]
(ucv.util/ui-error-boundary
{:body-fn (fn [] (comp/with-parent-context real-parent#
~(if (css-k? first-arg)
`(divs ~@body)
`(comp/fragment ~@body))))}))))
#?(:clj (defmacro error-boundary
[& body]
(error-boundary* body))
:cljs (defn error-boundary [& body]
(throw "Use error-boundary as a macro not a function")))
i can simplify that example if it’s too much
but essentially it’s just a component that catches exceptions in render and renders an error message
one of the major schisms is, in browser i need to use DOM in RN i need to use that other stuff
but ideally i want to re-use components and their queries
also .. how can you get any work done with all those reader conditionals? my brain hurts from looking at that little snippet
lol fair enough, sorry for the pain 😅
i’ll come up with something simpler
(div :.ui.container
(util/error-boundary :.ui.basic.segment :.ui.middle.aligned.celled.list.massive
(map ui-tax-list-item (sort-by :tax/title entities))))
it prevents errors from bubbling up
@currentoor why do you care if it’s react native or not?
^ exactly
@currentoor maybe better than to abstract that part away? for those common components you could have primitives that change according to the build (by using different source paths, with same ns name)
so you could have ui/block
instead of dom/div
/ rn/View
, makes sense?
different source paths with the same name? not sure what you mean
ex: src/ui-rn/my_project/ui.cljs
, src/ui-dom/my_project/ui.cljs
so the RN build uses the first, the DOM build uses the later, so when importing you get the right one
@thheller what you suggest instead?
well, in this case, we need a switch depending on the build, and one source has no relation to the other, they are like interfaces in my POV
(also worst, loading the wrong one may break the build)
you can use #?(:react-native (do-one-thing) :browser (do-that-other-things) :cljs (the-default-thing))
is there a way to define custom ones like this?
cool, I didn't knew that one 🙂
the only downside I see with that is lock on shadow, I don't mind, but for libraries I wouldn't do that
@thheller but how to deal with the fact that some required files will be available in one target but not the other? doens't that still require reader conditionals to do :require
?
that way you can ensure that react-native specific stuff is only loaded in react-native and so on
reader conditionals are pretty cancer. host specific stuff should be hidden behind protocols or something
and I'm curious, what's about the different source paths that triggers you so much? (asking because I have done that a couple times, and I can't see much problem from it, but I guess you have more context than I do)
well, they still are, I see them as a "implementation fork", for the compiler there is only one (unless you try to do multiple builds at the same time, which would be crazy with this setup)
@thheller this approach could work for functions but not macros
@currentoor I still haven't seen why this is a macro?
because it wraps the body in a try/catch
(div :.ui.container
(util/error-boundary :.ui.basic.segment :.ui.middle.aligned.celled.list.massive
#(map ui-tax-list-item (sort-by :tax/title entities))))
lol yeah, i should stop trying to have my cake and eat it too
those keywords generate nested divs
with those class names
right that’s the syntactic sugar the macro provides
but i agree it’s possible
you can use the macro of course, but that seems like a lot more code than adding #
😉
at risk of triggering you again, @wilkerlucio suggestion is looking attractive
adding RN to a browser project after writing a lot of code is proving cumbersome
@thheller thanks for the discussion, always helpful
oh wait if i do your approach, of putting the error-boundary-fn in the app config, then have the macro use it
then i can have it all without separating source paths
the differences in environment can be encapsulated inside that function and the macro just gives the syntactic sugar
@wilkerlucio @currentoor this is how I would recommend dealing with this https://gist.github.com/thheller/fa044450a6470fdd1afdbcf3f37a65e3
hope that makes sense somehow. the point is that this doesn't require ANY special compiler magic, config or reader conditionals. You just define a "common" shared interface in your code and provide separate implementations when needed
I like this solution idea, I would just don't do with records, a map with the fns works the same (I'm the one triggered by records/protocols :P)
yeah pure fns work just fine. I just use protocols whenever something is a bit more stateful
if you already have an env
you pass arround anyways (like in pathom
) things are even easier since you don't need to "interface" ns
switching :source-paths
is of course similar to this as it replaces the concrete implementation without defining an actual interface
yeah i like this approach too
thanks!
Has anyone seen this issue with shadow-cljs REPL (CLJS)?
(defn add "Add Two numbers" [a b ] (+ a b))
=> nil
(defn add [a b ] (+ a b))
=> #'io.example/add
nil being returned from defn when a docstring is present but the var returned when no docstring?I’m using goog.DEBUG
to conditionally run code in dev, but what’s a good way of conditionally requiring something?
I can see the dependencies for this code in the production build, even though the debug code has been removed
@chris547 interesting. works fine in the browser but not in node. not exactly sure why.
code stripping is probably not a good idea, it is kind of a nuclear option that shouldn't be used 😛
(ns foo.bar
(:require [dev.library :as d]
[goog :as g))
(defn thing []
(when g/DEBUG
(d/some-function)))
it works without yes, but the type hint slightly alters the generated code which means it works better with :advanced
but you can easily verify if its actually removed via
(defn thing []
(when g/DEBUG
(js/console.log "thing debug wasn't removed")
(d/some-function)))
@thheller - thanks for confirming I wasn't crazy - do you think this is a CLJS or shadow/node issue? I'm new to CLJS and don't know where to start looking
@thheller Okay, I'll revert to the previous version that I was using and see if it was working there to try to give a baseline
/**
* foo
*/
(function() {
cljs.user.x = function cljs$user$x(a) {
return a;
};
return new cljs.core.Var(
function() {
return cljs.user.x;
},
new cljs.core.Symbol("cljs.user", "x", "cljs.user/x", -156439873, null),
cljs.core.PersistentHashMap.fromArrays(
[
new cljs.core.Keyword(null, "ns", "ns", 441598760),
new cljs.core.Keyword(null, "name", "name", 1843675177),
new cljs.core.Keyword(null, "file", "file", -1269645878),
new cljs.core.Keyword(null, "end-column", "end-column", 1425389514),
new cljs.core.Keyword(null, "source", "source", -433931539),
new cljs.core.Keyword(null, "column", "column", 2078222095),
new cljs.core.Keyword(null, "line", "line", 212345235),
new cljs.core.Keyword(null, "end-line", "end-line", 1837326455),
new cljs.core.Keyword(null, "arglists", "arglists", 1661989754),
new cljs.core.Keyword(null, "doc", "doc", 1913296891),
new cljs.core.Keyword(null, "test", "test", 577538877)
],
[
new cljs.core.Symbol(null, "cljs.user", "cljs.user", 877795071, null),
new cljs.core.Symbol(null, "x", "x", -555367584, null),
"cljs/user.cljs",
8,
"x",
1,
1,
1,
cljs.core.list(
new cljs.core.PersistentVector(
null,
1,
5,
cljs.core.PersistentVector.EMPTY_NODE,
[new cljs.core.Symbol(null, "a", "a", -482876059, null)],
null
)
),
"foo",
cljs.core.truth_(cljs.user.x) ? cljs.user.x.cljs$lang$test : null
]
)
);
})();
(function() {
cljs.user.y = function cljs$user$y(a) {
return a;
};
return new cljs.core.Var(
function() {
return cljs.user.y;
},
new cljs.core.Symbol("cljs.user", "y", "cljs.user/y", 558816894, null),
cljs.core.PersistentHashMap.fromArrays(
[
new cljs.core.Keyword(null, "ns", "ns", 441598760),
new cljs.core.Keyword(null, "name", "name", 1843675177),
new cljs.core.Keyword(null, "file", "file", -1269645878),
new cljs.core.Keyword(null, "end-column", "end-column", 1425389514),
new cljs.core.Keyword(null, "source", "source", -433931539),
new cljs.core.Keyword(null, "column", "column", 2078222095),
new cljs.core.Keyword(null, "line", "line", 212345235),
new cljs.core.Keyword(null, "end-line", "end-line", 1837326455),
new cljs.core.Keyword(null, "arglists", "arglists", 1661989754),
new cljs.core.Keyword(null, "doc", "doc", 1913296891),
new cljs.core.Keyword(null, "test", "test", 577538877)
],
[
new cljs.core.Symbol(null, "cljs.user", "cljs.user", 877795071, null),
new cljs.core.Symbol(null, "y", "y", -117328249, null),
"cljs/user.cljs",
8,
"y",
1,
2,
2,
cljs.core.list(
new cljs.core.PersistentVector(
null,
1,
5,
cljs.core.PersistentVector.EMPTY_NODE,
[new cljs.core.Symbol(null, "a", "a", -482876059, null)],
null
)
),
null,
cljs.core.truth_(cljs.user.y) ? cljs.user.y.cljs$lang$test : null
]
)
);
})();
I just started working on a cljs node backend - but would this potentially break all code running in node (assuming it was documented with a docstring?)
but yeah I need to rethink the REPL code I think. thats what I get for trying to be clever I guess
So I just wanna check my understanding. shadow-cljs
and it's ability to work with package.json
essentially accomplishes the same thing you would get using cljsjs
libs with :foreign-libs
and :global-exports
?
I'm trying to figure out if I can find some way to get intellisense with material-ui
and was thought maybe the cljsjs path would get me that, but from what I've looked at it looks like cljsjs is just another way to package js libraries into your clojurescript app. Is that right?
And is there a way with Spacemacs or Cursive that I could get some kind of autocomplete for material-ui? Something like
(ns your.namespace
(:require [cljsjs.material-ui :as mui]))
(mui/Grid ...)
or something@thheller - Awesome, thank you so much for the fix - I'll update and hopefully will get back to learning more cljs 🙂
@trevor670 I'm not aware of aynthing that would get you autocomplete for JS deps
Yeah that's what I was worried about, when I stop and think about what that would mean I realize that would be a pretty dramatic accomplishment. It would have to like process typescript .d.ts
files to generate all the function definitions and stuff
There’s experimental support in CIDER for this with the recent clj-suitable completion package but there’s still some rough edges
I'm very new to clojurescript, I initially though cljsjs was something that was more involved (like cljs bindings or something) but I just realized that it's just automating config stuff
But based on what you said, definitely sounds like there isn't something I'm missing then! Wanna use material-ui I gotta just type it in
And that's the main sell of shadow-cljs right? Now adding npm dependencies is as simple as updating a package.json instead of doing the other config in project.clj and deps.edn? It's way closer to normal js dev workflows?
it is one of the features yes. wouldn't call it the main feature but it is one of them yes.
Yeah definitely does more than that! Thanks for all your work and answering my questions!