This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-04-13
Channels
- # announcements (3)
- # babashka (130)
- # beginners (73)
- # calva (22)
- # cider (46)
- # cljdoc (18)
- # cljs-dev (196)
- # cljsrn (18)
- # clojure (255)
- # clojure-europe (2)
- # clojure-finland (8)
- # clojure-gamedev (1)
- # clojure-germany (2)
- # clojure-losangeles (6)
- # clojure-nl (1)
- # clojure-spec (16)
- # clojure-uk (33)
- # clojurescript (32)
- # community-development (1)
- # conjure (40)
- # core-logic (11)
- # cursive (4)
- # datascript (8)
- # devcards (17)
- # emacs (21)
- # exercism (2)
- # fulcro (29)
- # funcool (15)
- # graalvm (18)
- # jobs (17)
- # jobs-rus (1)
- # lambdaisland (1)
- # lumo (1)
- # malli (19)
- # off-topic (15)
- # pathom (22)
- # quil (7)
- # re-frame (3)
- # reagent (3)
- # shadow-cljs (14)
- # spacemacs (41)
- # specter (2)
- # sql (5)
- # tree-sitter (1)
- # unrepl (16)
- # vscode (3)
- # xtdb (11)
- # yada (1)
Oh, works now. I tested different command and left that in the configuration, with proper npx command I see the output file. I think it would be good idea to print out the webpack output?
Though for watch mode it definitely makes sense to not show the webpack stdout. Anyway, I can just run the command myself if I want to see the output, and probably makes more sense here anyway, as I might want to pass the bundle to webpack directly or to Karma.
RE: not showing the webpack output, I think I've made a mistake in my webpack config and it's taken me a good 15m or so to realize that's why cljs is silently not producing output.
If the command fails, the exception should contain the stdout contents.
Didn't fail, I think I didn't have webpack installed (I guess I wiped my node_modules). And it was prompting for input.
Yeah. And I think it won't show stderr contents.
The implementation closes stdin, so it should exit?
Something (possibly) useful is that you don't need a http://webpack.co
Interestingly, while the bundle output is smaller for react/react-dom, shadow still produces slightly smaller output. (1KiB difference).
For a simple prn, running through webpack seems to save some bytes:
prn/Cljs 1.10.520-fn-invoke-direct.js 90KiB 21KiB 18KiB
prn/Cljs 1.10.520.js 92KiB 21KiB 18KiB
prn/Cljs 1.10.597-fn-invoke-direct.js 93KiB 21KiB 18KiB
prn/Cljs 1.10.597.js 94KiB 21KiB 18KiB
prn/Cljs c1cf559a Bundle-fn-invoke-direct.js 90KiB 20KiB 17KiB
prn/Cljs c1cf559a Bundle.js 91KiB 20KiB 17KiB
prn/Shadow-Cljs-2.8.94-fn-invoke-direct.js 90KiB 20KiB 17KiB
prn/Shadow-Cljs-2.8.94.js 92KiB 20KiB 17KiB
For Reagent demo site, just using Cljsjs React is 20kB smaller
For very simple:
(ns io.dominic.cljs-sizes.main
(:require
[reagent.dom :as rd]))
(defn welcome
[name]
[:h1 (str "Hello, " name)])
(defonce _mount (rd/render [welcome "Fred"] (js/document.getElementById "app")))
I get slightly smaller bundle (by 8 bytes) for the webpack version.I didn't have elide-asserts enabled for bundle builds, sec
466KB now, with Cljsjs 457KB
ah no, that is not cljsjs version
I'm expecting bundle to win because it does not have so many duplicate dependencies as cljsjs creates.
reagent website in fact uses React with node module processing, so React code is going through Closure optimizations
node module processing 457KB, bundle 466KB, foreign-libs 470KB.
Makes sense now
No, React is probably one of the only libs that works and even it still requires the extern file.
Extern file also means that it isn't properly optimized, we should have separate extern file for this, with only the dynamic names that break when optimized, but not the public API which can be optimized in this case.
without the extern file, node module processing output goes down to 453KB (but it doesn't work)
Should ^:export
work in advanced optimized bundle, so that the exported fn can be accessed from the JS side? Looks like goog.global
is not the same as window
and looks like it is not seen outside of Cljs module.
Maybe Cljs should set goog.global = window somewhere, goog.string.unescapeEntities
also breaks as it doesn't find goog.global.document
.
https://github.com/google/closure-library/blob/master/closure/goog/string/string.js#L569
With few inferred extern additions, I now have Reagent tests passing with advanced optimized bundle.
@juhoteperi @dominicm re: :bundle-cmd
output I really don't care that much here - it's just important to note it's a convenience
as noted you can easily run the bundle cmd separately and that might be common because you want to do more things
One problem is that I don't understand how bundle relates to development mode at all
Well, I have this "worry" that it'll be run more frequently than just once. So dev would be very inconvenient otherwise.
Bundle-cmd is run after every compile. Or I guess you could use JS tools to watch for file changes.
In watch mode, on file change.
Okay. One worry I have is webpack failing mid-development session. That would be a bit of face meeting wall to realize/debug.
that possible but I don't think it's much of a worry - you need the bundler when your ClojureScript requires include something from node_modules
you haven't required before.
also nothing is stopping anyone from using watchman and setting this up however they like
@juhoteperi we could change ^:export here, goog.exportSymbol
takes a third parameter - I'm wondering if that's good enough
Closure library code which presumes goog.global
is the window object is also a problem, that wouldn't help with that
@juhoteperi ah yeah, ok we have the hook for this in core, only used in Node.js right now
@juhoteperi try master
Maybe webpack has some way to handle this?
the "problem" is that goog.global = this
breaks because this
is no longer window
after webpack has processed it
assigning goog.global
in cljs.core is also too "late" given that it may have been used before loading cljs.core
by closure code (eg. goog/base.js)
Cljs should probably use similar logic to select the global object?
that is used for :npm-module
which is a rather special :target
in that it exposes each CLJS ns as a separate require("shadow-cljs/cljs.core")
ns
but I think the next version of webpack will remove the process
polyfill so that would break then
@dnolen Export & unescapeEntities
works now (but I had other problems with Karma)
maybe instead of checking for process.browser
the global object could be just window || self || global ?
@juhoteperi I don't really know what you're trying to suggest
^ related to @thheller comments
Yeah, maybe it is best to leave goog.global
intact by default? MAYBE option to set it to something IF webpack or other tools don't have better way handle this. It depends on where the bundle is used, what the global object should be.
Another option instead of mucking around with goog/base.js
- we could just make a new compiler option to set this
@thheller I've never personally seen cases where setting target at the end of core.cljs is a problem - do you have something specific? this after implementing in a bunch of environments, browser, Node, React Native etc.
but it used to be a problem that the goog.define
in goog/base.js
would then be defined in another scope
Webpack has way to set module this
to window
: https://webpack.js.org/loaders/imports-loader/
@juhoteperi right but this will be different in every bundler - I'm ok w/ having a way to control this because ^:export
is critical
Yeah. Separate compiler option sounds ok to me.
https://github.com/clojure/clojurescript/commit/f95a13b3e3b193749e10e15d95ef98506ce3be29
@juhoteperi let me know if that works for you
@dnolen It works
draft announcement for the new bundle target - https://github.com/clojure/clojurescript-site/blob/april-release-target-bundle/content/news/2020-04-13-bundle-target.adoc
a lot of the changes/fixes to master were based on a new React Native tool I've been working on for the past three weeks
if you're curious how to leverage cljs.main
and the new build target in a custom build tool - this is worth checking out š
re: krell, very cool. it looks like it doesnāt support hot-loading yet; do you use a REPL workflow atm?
Iām v interested in creating an easy hot reloading experience that takes advantage of the new react-refresh package. Itās a little tricky tho.
react-refresh requires that when changing a file, all files in the dependency tree up to the root will be reloaded. This is to support e.g. changing a function or constant in a file, that gets required by another file, that then gets required and used in a component, will properly invalidate the component and trigger React to re-render that component. figwheel and shadow-cljs typically takes the simpler approach of only reloading the file changed and itās direct ancestors because we assume everything is in the global scope and that we donāt do side-effects on reload (which react-refresh is essentially a side-effect on reload)
re: REPL - that's mostly what Krell does - REPL stuff - the bundler stuff is just reusing ClojureScript master
I feel like if I could figure out how to handle these react-refresh cases from a REPL, it would make everything else easier
the āside-effect on defn
ā pattern that react-refresh heavily encourages makes it more difficult
@lilactown I think the only thing is to not swim upstream
the point is you cannot preserve the REPL state - but depending on what you're doing who cares?
the point of auto-reconnect is that you can just follow all the usual React guidelines
yeah, I want to preserve the behavior you get when enabling āFast Refreshā in React Native
I'm not saying of course that react-refresh works - just saying that's definitely one of things I'm interested in doing different w/ Krell
disabling React refresh should be entirely an optional thing because you want to do a stateful REPL session
I think thereās two kinds of refresh in RN: a full reload of the app bundle on change, and the new āFast Refreshā feature IIRC
The āFast Refreshā mode preserves state across reloads - even local component state - but requires you to generate signatures for each component that get invalidated on file change. this is done by a babel plugin that RN gets shipped with, which obviously wonāt work with CLJS
a react wrapper lib (like helix/reagent/etc.) can generate these signatures using a macro, but requires integration at the reload step to properly invalidate them like I was saying
yeah, I dunno I'd have to look more closely at how it works to have any clearer thoughts
@lilactown but one question I have is how is react-refresh really any better than just developing a RN Reagent app over a state atom w/ Figwheel / shadow / soonish Krell?
In a web app this often not that fun - but for shallow view graphs in mobile I don't see the problem
if you take the stance that local state and global state are functionally and architecturally identical, I agree with you.
that's all I'm interested in though - if the end result is same performance, same experience
I haven't followed along but you also have Om inspired projects - the reconciler does what you're talking about
Om's idea is that you have global state - but the reconciler gives you precise updates
especially with concurrent mode unlocking more perf and experience knobs for devs, which works best with local component state
like I said I don't have a strong opinion and I personally think there are alternatives
reading over https://reactnative.dev/docs/fast-refresh it doesn't seem very simple and lots of caveats - so meh
Havenāt dove down to fast-refresh for React Dev yet, but I can comment on the state bit - my goal is to take advantage of the new React Concurrent mode when it lands. So far, and until some new docs land, React wants to you let it handle state (there was months of churn to figure out how this could work with global state).
This also impacts you more the more you rely on hooks, and the more you rely on JS components that use hooks. Fast-refresh promises to keep state from hooks and rerender, for free. Seeing that esp. for UI work local state makes some sense, preserving that makes sense.
@orestis yeah I haven't followed the Concurrent mode stuff that closely - it does look interesting - but I don't know what you mean by "React wants you to let it handle state"
what he means is that, Concurrent mode works best when your state is coupled to an instance of a component.
construction/destruction relies on mount/unmount, updates to state mean the component re-renders, etc.
concurrent mode allows React to pause rendering a tree and resume rendering that same tree later. this creates problems if the state that was rendered in multiple parts of the tree has changed in between pausing and resuming
React can handle this in a transactional fashion if you manage state inside the component instance. it essentially puts state updates on a queue that gets computed on the render that it triggers. if you store state outside of a component instance, then React canāt handle those state updates for you - if you update an external state atom in a naive synchronous way then you can end up with parts of your tree that were rendered using an old atom state, and parts that were rendered using a newer atom state. if you store state once, and it lives in a component, then resuming the tree will either re-use the state updates already computed inside that render, or compute those state updates against the latest state. you avoid the sort of coherency problems that state external to the tree can introduce.
react is adding more tools to handle external state but the happy path is definitely storing it inside a component using useState
/ useReducer
etc
yeah gonna have to look at the docs more - thanks for summary's though - helpful
The most annoying thing is that this is very much in flux. This approach for examples invalidates all the major React global state managers (redux, relay, Apollo). Thereās an RFC called useMutableSource IIRC that seems to handle this, and to my knowledge @mhuebert did a POC some time ago for ClojureScript. Iāve kind of decided that Iāll wait until an official release or more guidance is available.
My only fear is that as with fast-refresh, the React devs will end up with just a Babel/webpack plug-in that we will have a hard time incorporating into our dev workflow.
If I had time to focus on this Iād definitely try to reach out to the React team, they said they wanted to release concurrent mode under an experimental release so that tooling can catch up. Sadly my day job leaves zero time for this kind of low level stuff.
But to be fair to them, they are trying the best and using the new Facebook as a guinea pig.
The plan AFAICT is that the official release will be simultaneous with major libraries so the change for consumers will be mostly under the hood.
well this still seems a bit far out to me and upgrading legacy applications seems like a monumental task
yep š all of this CM talk is just motivation for why I desire good react-refresh integration
I expect that libs and apps that use non-CM safe patterns will continue to be supported for a long time. there are a lot of different āmodesā that allow devs to tune React to what works with their code
Actually the new bundle stuff sounds like we can finally start releasing tools that depend on React and other npm stuff more easily.
I totally wish React had put Concurrent into a new lib with different name. I am just glad that this useMutableSource stuff is finally arriving. Looks to me like it is going to work well.
@dnolen was there a tools/repls page that got removed on the cljs site?
@alexmiller which one? I deleted a lot of stuff
yeah, guess so - that was the top nav link for tools, so this broke a bunch of nav
https://github.com/clojure/clojurescript-site/commit/1aff4034a39b83fddb3472bb5df140c7603604bd
@alexmiller you're not talking about internal links right?
I'm talking about the top level nav - the Tools link went to that page
I'm fixing it up now that I understand what happened
I thought I broke it :)
ah k, I didn't understand nav worked that way - it shouldn't have been the top link at this point
I can easily add redirects from old links to somewhere new too if you run into that, there's a list of those in the deployment
should be fixed up now - there's now a tools/tools page which is in the clojurescript-site that mostly matches the left nav
@dnolen not sure if you are monitoring ClojureVerse, some comment left there on Node versions: https://clojureverse.org/t/test-drive-the-beta-clojurescript-js-bundler-integration-support/5779
@orestis chimed in, not sure what's going on there seems similar to what @juhoteperi but I think missed the root cause
Related to JS modules, I've been already thinking about adding notice to Cljsjs page and repository strongly suggesting using other ways to consume JS libraries. Now is even better time for that.
@juhoteperi yes, but let's wait for the release
I close this one old module processing issue, couldn't reproduce the problem now: https://clojure.atlassian.net/projects/CLJS/issues/CLJS-2836
I think https://clojure.atlassian.net/browse/CLJS-1543 this is now done (goog.module support)?