This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-11-21
Channels
- # announcements (1)
- # beginners (20)
- # biff (5)
- # calva (43)
- # cider (5)
- # clj-commons (7)
- # clj-kondo (11)
- # clojure (58)
- # clojure-brasil (1)
- # clojure-denmark (1)
- # clojure-europe (27)
- # clojure-nl (1)
- # clojure-norway (13)
- # clojure-uk (2)
- # clojurescript (71)
- # data-science (32)
- # datalevin (6)
- # datomic (19)
- # emacs (1)
- # gratitude (3)
- # honeysql (8)
- # hoplon (15)
- # hyperfiddle (3)
- # introduce-yourself (1)
- # lsp (19)
- # malli (4)
- # nbb (7)
- # other-lisps (5)
- # practicalli (1)
- # re-frame (14)
- # releases (1)
- # ring-swagger (1)
- # squint (118)
- # xtdb (9)
- # yada (2)
Field report on compiling a small artifact via shadow-cljs
versus squint
.
shadow (8.6k):
-rw-rw-r-- 1 chrism chrism 8.6K Nov 14 20:25 twge.js
squint (via vite) (11k):
-rw-rw-r-- 1 chrism chrism 11K Nov 18 18:07 _source/public/dist/assets/index-ye3oH3AF.js
This is with cljs that bends over backwards to be small doing all kinds of silly things to avoid native cljs datastructures. If I refactor it to look like more normal cljs the squint asset will be far smaller as the shadow asset will be ~100kb.
https://github.com/chr15m/tiny-web-game-engine/I'm very impressed that you managed to produce such a small artifact from full-blown ClojureScript/Shadow-cljs 🙂 Is the refactored version available to look at too?
I haven't had a chance to re-write it back to more traditional cljs but I know the size because every time I took a small step wrong and used e.g. =
the size would balloon out. 😁
So for example if you change coercive~=
to =
you will get a much larger artifact size.
If you're interested in the hoops someone has to jump through to get it that small I wrote a post detailing it here: https://mccormick.cx/news/entries/clojurescript-uis-in-500-bytes
Oh, nice! Thank you.
Did you produce the final artifact using vite build + minify? There’s some stuff that can be done to make the size somewhat smaller. 11kb is still pretty small for an app
I did vite build and tried to run it through closure (which failed). Let me try some different minify options.
Yes 11kb is still very small! 🤏
I'll have to figure out which minify is the one that actually works these days.
You can just look at the dist output to see what it looks like, if it looks like a garbled mess, it’s minified
Ah ok yep it's minified already. I don't think Vite does the kind of tree shaking that closure does by default but maybe with squint type of architecture it doesn't matter anyway.
I’ll have a look tomorrow at the output myself, there’s probably some improvements to be made. Closure is pretty clever though
Oh wait, the difference is only like 10-15%? I misread. Doesn’t seem that significant to me, in squint you don’t have to hold back that much though :) aset is still something that can be made shorter. If you have tiny examples of stuff that stands out, feel free to post issues
You can configure clj-kondo to warn on certain function usages like = and suggest an alternative, it’s called discouraged var.
Yeah very small difference. I probably need to check my life choices spending so much time on these kb. :face_holding_back_tears:
With squint on master, managed to make it somewhat smaller ;)
✓ 19 modules transformed.
dist/index.html 1.29 kB │ gzip: 0.76 kB
dist/assets/index-NPFnc9NS.css 0.84 kB │ gzip: 0.41 kB
dist/assets/index-140jzfIg.js 10.42 kB │ gzip: 4.34 kB
After processing it with closure, I can squeeze another 1kb off it ;)
$ npx google-closure-compiler --js=public/dist/assets/index-140jzfIg.js --js_output_file=dist/out.js
$ ls -lat dist/out.js
-rw-r--r-- 1 borkdude staff 9459 Nov 28 10:32 dist/out.js
(note that I had to wrap the entire output in a self-calling async function because closure doesn't support top level await)
The whole file fits in one screen on my 13" laptop so it's probably not hard to notice the differences
these are the closure vs squint js outputs optimized and then pretty-printed again: https://gist.github.com/borkdude/5dce5e97bc8052b95c37644759dfa07d let's see...
I think a little bit more of squint's stdlib is imported which may explain the difference
Wow! 10kb. This is great. That means I can maybe get small size without the small size compromises I had to make with full blown cljs. Thanks, I will definitely take a look at this next time I'm on the game engine project.
last time it was 11kb, so not that big of a difference but now it's only 1.4kb difference with full blown cljs with restrictions ;)
I think I know what's happening: I can probably optimize the js-interop macros a little bit more such that they won't use the squint stdlib at all and then you should end up in the same ballpark (1.5kb less) but the idea of squint is not to avoid the squint stdlib per se
@chris358 Ah got it. When optimizing those js-interop macros to avoid the stdlib I get this which is similar to "full blown shadow with Closure with avoidance of any CLJS core library function"
dist/assets/index-Cx3tSDMb.js 8.97 kB │ gzip: 3.81 kB
hi guys! Is it possible to writing react-native (expo) apps using squint? Maybe someone has experimented with this? Like https://github.com/PEZ/rn-rf-shadow
I haven’t tried but I’ve been wondering about this too. Take a look at the examples/vite-react directory and perhaps it’s possible to adapt this to a RN example
perhaps @U0ETXRFEW can make a squint example too :)
@guliy How are you using the REPL?
For RN I'd just use npx squint watch
with {:paths ["src"]}
in squint.edn
Well, I wanted to be able to work with repl like I’m used to doing in clojure, but I see that it doesn’t work that way (
The nREPL works for Node.js currently, but it's kinda limited to 1 file, it's still in development. I don't know how a REPL for React Native works, what environment does it use?
you can make it work when you rewrite the imports yourself from symbols to ["./relative_file.js"]
I intend to fix this within the next two months or so, it just takes time to mature stuff :-D
It’s works!! thanks, @U04V15CAJ :hugging_face:
Does anyone here has experience with vite on a non-react project? It does a full page reload each time I edit the JS but I want to preserve some state (the state of a text editor)
what framework are you using? you usually need a third party plugin that can tell vite specifically what should be hot reloaded
hmmm I think you will need to write a plugin then using this https://vitejs.dev/guide/api-hmr
maybe helpful https://dev.to/omar4ur/vite-hot-module-replacement-a-complete-example-pkg
Here's a brief example of how to use HMR's API in a Vanilla JavaScript project:
Let's say you have a module message.js:
export function message() {
console.log('My Message')
}
And you import it in main.js:
import { message } from './message.js'
function app() {
message()
}
app()
// Handle HMR
if (import.meta.hot) {
import.meta.hot.accept('./message.js', () => {
app()
})
}
seems like accept registers a callback for when a file (module) is updatedanother way of doing this btw is just to make lots of small files, vite should only rerun the ones that have changed
so if you don't touch the index file which renders the root components but touch some 'subcomponent.mjs' it won't rerender everything - I think it does this out of the box
personally I get very annoyed by js projects where every single component is its own file but I think this is the reasoning behind it. though these days react etc have plugins which keep track of named functions and can surgically reload just the ones that have changed (though you have to follow some rules like splitting regular exported functions and components into diffferent files, at least you don't end up with 10000 files)
even when I have a small file
./editor.mjs
when I add console.log('editor');
there, and only touch that file, the whole page still gets refreshedThe part I don't understand about this accept business is, what exactly happens here:
if (import.meta.hot) {
import.meta.hot.accept('./message.js', () => {
app()
})
}
I just don't want to refresh the whole page and don't remount the editor, that's all 😭
import.meta.hot.accept('./message.js', () => { app() })
: If HMR is enabled, this line is saying, "please accept the hot updates of the module './message.js'", and when there is an update, execute the provided function { app() }, which means rerun the app function.
I think
that's what I understood too, but how do you prevent an editor from re-mounting in this scenario for example
yeah it shouldnt, assuming the page doesn't reload... apparently doing
if (import.meta.hot) {
import.meta.hot.accept();
}
should be enough to stop a full page reload, but if that doesn't work perhaps you can use some persisted state management so that the editor contents is maintained even through a page reload?oh nice, this prevented the hot reload!
if (import.meta.hot) {
import.meta.hot.accept();
}
This works!
if (import.meta.hot) {
import.meta.hot.accept();
}
function initEditor() {
if (globalThis.editorLoaded) return;
new EditorView({state: state,
parent: editorElt,
extensions: extensions });
globalThis.editorLoaded = true;
}
initEditor();
Thank you!Here is an online squint REPL. Use Cmd-Enter after an expression to eval :-D https://snapshots.nextjournal.com/clojure-mode/build/8ffba58d941dda80fffb58fcf9fd506003dbdc24/squint/dist/index.html
alt+enter isn't working for me, but windows+enter ("MOD + Enter" / cell eval) works great - awesome stuff!
cool, that's activated the alt+enter keybinding, but the wires seem a bit crossed - like currently I would say the following is an accurate description: > mod+enter is doing "at cursor" > alt+enter is doing "cell" > alt+shift+enter is a noop
@U899JBRPF does it look ok now? also fixed top level eval https://snapshots.nextjournal.com/clojure-mode/build/c3a8a368383f16bf699cc3cf603d5f8211cfcf4d/squint/dist/index.html
nice, almost! the instructions now say ctrl, but I have to press windows (mod?) instead -- otherwise it all works as expected 🙂
@guliy found out how to do React Native with squint: https://clojurians.slack.com/archives/C03U8L2NXNC/p1700582330957469?thread_ts=1700544057.865359&cid=C03U8L2NXNC


What I’m after is something that let’s me code in Clojure, but can run in a Java environment where the Clojure runtime can’t run. Don’t know how squint-ish that is, but it seems to be part of it that wherever javascript runs, I can use squint to write the program, without the overhead of a runtime.
> What’s a royal ball? After all, I suppose it would be frightfully dull, and boring, and completely ... completely wonderful. — Cinderella
perhaps somebody has compiled java to wasm?
oh wait that's completely the wrong thing never mind
ok this one turns java bytecode into wasm: https://github.com/i-net-software/JWebAssembly
@Export
public static void main() {
Document document = Window.document();
HTMLElement div = document.createElement("div");
Text text = document.createTextNode("Hello World, this text come from WebAssembly.");
div.appendChild( text );
document.body().appendChild( div );
}
what a monstrosity. "java"script comes full circle.> CheerpJ is the only solution which can run any large-scale, unmodified Java applications, applets, or libraries in the browser. No downloads or plugins required. https://cheerpj.com/
> Compiles Java bytecode to JavaScript, WebAssembly and C https://github.com/konsoletyper/teavm
imagine clojure -> java bytecode -> wasm -> compiling clojurescript -> javascript 😂