This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-11-05
Channels
- # announcements (5)
- # beginners (49)
- # boot (9)
- # calva (34)
- # cider (9)
- # clara (18)
- # clj-kondo (1)
- # cljsrn (7)
- # clojure (196)
- # clojure-dev (4)
- # clojure-europe (11)
- # clojure-france (3)
- # clojure-nl (13)
- # clojure-norway (2)
- # clojure-spec (8)
- # clojure-uk (85)
- # clojurescript (87)
- # clojurex (202)
- # core-logic (6)
- # data-science (6)
- # datomic (9)
- # dirac (1)
- # duct (93)
- # emacs (9)
- # events (1)
- # fulcro (22)
- # hoplon (30)
- # jackdaw (10)
- # keechma (1)
- # leiningen (5)
- # malli (17)
- # off-topic (9)
- # other-languages (3)
- # pedestal (7)
- # re-frame (3)
- # reagent (5)
- # reitit (9)
- # remote-jobs (6)
- # rewrite-clj (80)
- # ring (2)
- # shadow-cljs (191)
- # tools-deps (54)
- # vim (14)
- # xtdb (7)
Ok. What I'm doing right now that works is:
:reql/quit
(shadow/repl-runtime-select :browser-extension
(:runtime-id (first (shadow/repl-runtimes :browser-extension))))
(shadow/repl :browser-extension)
No worries, I realize I'm way in edge-use-case land here, but it's interesting nonetheless.
it currently only expects that as part of the nrepl message (which no tool actually sends)
No worries at all, I appreciate that it works as well as it does. I'm not really impeded here, just trying to make it more ergonomic so I can eval in different JS environments really efficiently.
https://github.com/thheller/shadow-cljs/commit/7af6e7fc290871431611b88e31bd3f56e88c58e3
It might require more tooling integration (eg from Cursive) but in my "ideal future" I'd be able to configure rules such that "send to repl" from a certain file automatically gets eval'd in a particular environment, rather than switching a session singleton. I'm not sure how practical such a system would be beyond what I'm doing, but maybe there's a more general "route eval requests to environments" behavior hiding in there.
but the regular CLJS repls don't expose them so it would be shadow-cljs only right now
actually, before I type all this out and bother y’all with thinking through this, I’m going to do some experiments and see where I actually fall over
the problem I’m currently afraid of is if I use React’s way of performing an update without restarting the app, it might have some stale references to e.g. a utility function I just changed
so I might need some way of determining what “kind” of change was done (in webpack they use some heuristic to determine of a file only exports components), and decide whether to run performRefresh
or render
after writing it out, I'm pretty sure this will be an issue I need to tackle if I want to use the React hot reloading stuff
I don't suppose there's any information that the before-load
/`after-load` fns can be given to help with this?
since all def
and defns
are exported by default, I don't think I can rely on that heuristic to ensure only the React components in a particular file are used elsewhere.
I'm thinking right now if I could annotate an ns with some metadata, and get fed that in my after-load
hook, then I could rely on the programmer to reasonably discern if a ns is safe to just refresh and add the meta themselves
ah interesting. I was reading figwheel docs, and it distinguishes between reloading and recompiling dependent namespaces. it's possible that by reloading dependent namespaces (not recompiling them) we can have the desired behavior.
e.g. if I have namespaces a
, b
and c
where a
requires b
requires c
:
a -> b -> c
then if I change c
, only b
is reloaded.
if we reloaded the entire dependency tree of c
-> b
- a
then I think things would work correctly. I don't know how heinous that would be in large apps 😬
is there any example that shows how to call my component in react component, my own component is defined in cljs
@lilactown the last time I looked at the fast-refresh stuff it required processing the generated JS to extract the signatures of the hooks used
The only actual requirement is to register the signatures with the runtime somehow (Babel, macros, etc.) and to call a method to trigger a "refresh" on hot reload
The last real hurdle is to correctly get all references to my changed ns to reload correctly
@haiyuan.vinurs how did you define your component? how is the JS code getting access to it? :npm-module
+ webpack?
i’ve solved the problem, in cljs ns pages.dashboard.questionnaire
(def ^:export QuestionnairePage
(r/reactify-component
(fn [a]
(println "questionnaire page is" a)
[:div "questionnaire page"])))
and in js
const { QuestionnairePage } = window.pages.dashboard.questionnaire
and i can use the component like this
<QuestionnairePage a="b" c={style} />
Hi - I have 2 npm packages generated by shadow-cljs that are a dependency of a third application. Is it possible to share the timbre configuration between the two packages in the packaging project?
it is not advisable to create 2 npm packages intended to be consumed in one project. they'll contain a lot of duplicated code
I have a .clj
file with a macro in it under src/asciidocs/core.clj
, and then I have src/asciidocs/app.cljs
file which declares (:require-macros [asciidocs.core :as core])
. When I try to use the macro the shadow-cljs complains:
6 | (println (core/load-asciidoc "resources/docs/cv.asc"))
------------------^-------------------------------------------------------------
No such namespace: asciidocs.core, could not locate asciidocs/core.cljs, asciidocs/core.cljc, or JavaScript source providing "asciidocs.core"
--------------------------------------------------------------------------------
I am not sure why this doesn't work. I thought I was supposed to have my cljs macros in a clj file like this.read https://code.thheller.com/blog/shadow-cljs/2019/10/12/clojurescript-macros.html
thanks
Is there any way to get the compiler to not evaluate stuff in #?(:clj
blocks? I don’t want to compile the required NS into CLJS just to get rid of an error, as it’s not otherwise used in this file.
Of course, I can just require something random, like #?(:cljs [clojure.string :as pc])
to get around it, or create a dummy NS to include for this purpose.
I'm confused about your question. you ask about :clj
blocks but then fix it in :cljs
block?
it is the reader failing here. not the compiler. it isn't eval'ing the :clj
blocks. it is just trying to read and for that is must resolve the ::pc/input
alias
Ah, crud. Alright, I’ll make a dummy NS to use in these cases.
I thought I’d colocate the defmutations
for both frontend and backend to the same file for fun and profit.
might be interesting to modify tools.reader so it doesn't try to resolve whenever it is in a read-cond block that isn't active
nice result from the wild: I recently converted a fairly large client codebase to compile with shadow-cljs - live-reload became ~3x faster and more reliable (was previously getting lots of errors when refreshing the page during recompile)
Speaking of the wild, I don’t want to use the words “de facto cljs compiler” yet, but I feel like I’m seeing a lot more shadow-cljs out there these days as opposed to only a year ago or so. Have you seen any concrete indications as to this @thheller (Visitors to the site etc.)?
Thanks, that seems to work, but I'm using spectron (https://github.com/electron-userland/spectron), which is supposed to open up an electron instance to run the spec with webdriver. But running the spec in the REPL doesn't do that so maybe it's not possible from the node-repl?
so node the-output.js --only some.test/foo
or so. if someone is interested in working on that
hey thheller, I made a short POC of what I'm trying to do with react's HMR support (react-refresh) here: https://github.com/Lokeh/react-hmr-cljs
I'm able to generate the signatures and get hot reloading of same-file changes working just fine
however, in the example I have a dep-b
and dep-c
namespace where core
depends on dep-b
, and dep-b
depends on dep-c
.
if I change the greeting in dep-c
to be "Bonjour!"
, it won't pick up the change on hot reload until the next render
now you only need to figure out how to get rid of all the related code in production 😉
don't know enough about the refresh stuff to comment. it likely happens because core didn't change it just cancels the re-render
right, so the issue is that the react-refresh stuff only re-renders components that have had their “type” (the function component registered with the ID) changed
the way that they’re suggesting people implement it is to detect whether a file only exports components. if it exports anything else than components, then call React.render
instead (and blow away the state)
I would need to be able to somehow detect whether a namespace is “safe” to refresh in my after-load
hook
sorry, don't know enough about react-refresh to make sense of that. Don't know why "only exports components" is relevant at all. makes no sense to me right now
it’s a super dumb heuristic to try and detect, “does anything other than React call something from this file”?
when i require ant-design in cljs and build a browser target like main.js, is that all ant-design files will bundled in the main.js?
@haiyuan.vinurs yes if you require antd
directly
if I put what I’m asking for concretely, with my understanding of the problem: 1. would you be willing to add the ability for the load hooks to get passed any info about the ns that’s being loaded? symbol + metadata? 2. would you be willing to add a setting that would reload all dependent namespaces up to the entry point?
either 1 or 2 would potentially allow me to solve the reloading problem. 2 I feel like might have the most potential for problems, but I don’t know the complexity for 1
1) the client side doesn't have that info. 2) I kinda chose not to add it https://github.com/thheller/shadow-cljs/issues/349
I mean react-refresh is not even released yet. not going to add features for it until the design is finalized
the way I understand react-refresh to work today, is each time a component is registered (e.g. via a reload of the namespace), if the ID already exists it marks that ID as dirty.
when performReactRefresh
is run, it then goes through the React tree from the root and re-renders all of the components marked as dirty.
if the hooks “signature” has changed since the last refresh, it will re-mount the component instead of re-rendering.
so the issue shows up when: - a function gets changed that some component uses inside of it’s render that’s not a registered component - that component’s namespace wasn’t reloaded, so it’s not marked as dirty - React doesn’t re-render it since it thinks it’s clean
OK that's what I've been doing but I'm having trouble switching back to having the logging active after doing a release build. TBH I think I'm not clear on the correct way to do a release build, deploy it to the server, and then go back to developing.
https://shadow-cljs.github.io/docs/UsersGuide.html#_release_specific_vs_development_configuration
if you put it into the incorrect places or access it incorrectly the cache won't be invalidated
When I run shadow-cljs release
, it overwrites the same main.js as shadow-cljs watch
- is that expected?
OK, maybe that's the simplest way to fix the issue then - I'll have the release build go somewhere else
And I access it with (get-in @cljs.env/*compiler* [:options :external-config :logging])
ok looks good then. release
builds don't share any cache with the watch/compile
builds so they usually don't interfere
is there a way to specify the :lein
option outside of the top level? context is that im trying to use lein with shadow and specify a different lein profile for :dev
and :release
within a build
@thheller thanks so much. I'm once again amazed by how available you are to help out. Don't you get swamped?
I went ahead and opened an issue on React to try and see if we can solve this without needing to change shadow-cljs 😛 https://github.com/facebook/react/issues/17281
@lilactown so is the concept of react-refresh that re-registered components get refresh, but nothing else does?
re-registered components get re-rendered (possibly re-mounted if the hooks signature has changed), otherwise all components are left alone
so it's essentially partial dependency invalidation
which kinda makes sense because react only know how to invalidate and hot-reload a certain class of things, namely components
react is saying "if you tell me when my stuff changes, I can make an honest effort at refreshing them because I know their semantics"
but that's always been the problem at HMR in general... your app needs to work in a way amenable to HMR
HMR concerns bleed into the application architecture
The thing that puzzles me right now is that, if React re-rendered the entire app, it would fix this most of the time I think
re-rendering the whole app is the sort of hot reload shadowcljs already has today, right?
If you could re-render the whole app without losing local state, you have achieved the dream 😂
and this refresh thing re-renders without re-mounting?
then I think what you need to make this work is the dependency graph that shadowcljs has around
then you can reason about it and say things like "namespace.path changed so I will mark all components that depend on it dirty"
but I guess that's why you wanted the component metadata in the first place?
if you go down to the symbol level and still have the dependencies it might be easier
then you could have some reverse mapping that says "this react component in refresh stuff maps to that cljs function" and also "this reload affected all these cljs symbols" and cross reference those
yes, you can take the surgical approach where you analyze each components dependencies and detect whether any have changed
you can also take the sledgehammer approach, which is that shadow-cljs or figwheel could reload ALL dependent namespaces which would in turn mark any components within them as dirty
both of these require quite more work on the tooling side, which I understand why thheller is skeptical 😉
each component gets roughly this emitted:
(def set-my-component-signature! (create-signature))
(def my-component ...)
(set-my-component-signature! my-component "hooks signature goes here")
(register! my-component "my-app.core/my-component")
I’m not sure how I would make it dirty. I suppose I could wrap each in a fn? that seems gross
dunno either. does it use identity?
like regular react or does react-refresh have different rulres?
(register! my-component "my-app.core/my-component" (fn [new-component] (set! my-compoent new-component))
maybe
I think the gist of the thing is to give the call to react-refresh-runtime/register
a new function