Slide 1

Slide 1 text

Michiel Borkent @borkdude Dutch Clojure Days 2022 ClojureScript Reimagined

Slide 2

Slide 2 text

🪶 Light-weight (memory) ⚡ Fast to start 
 👩🔧 Easy to install & operate (single binary) 🔋 Batteries included

Slide 3

Slide 3 text

Can we fulfil new Clojure use cases with similar approaches? What about (CL)JS targets? JavaScript as universal glue language

Slide 4

Slide 4 text

ClojureScript Is awesome 🙏 Targets JavaScript ecosystem with Clojure Google Closure: advanced whole program optimizer Mostly used from JVM Self-hosted exists (8mb of JS, non-advanced) Are there are opportunities?

Slide 5

Slide 5 text

Support for ES6 modules Async/await Compiling ad-hoc small snippets (scripting, on the fl y compilation) (Also) run without JVM, fast startup for small scripts JSX Distributing compiled CLJS libraries on NPM for usage from JS Interop improvements: JS destructuring CLJS Opportunities

Slide 6

Slide 6 text

Small Clojure Interpreter $ clj 
 Clojure 1.11.0 user = > (require '[sci.core :as sci]) nil user = > (sci/eval - string "(+ 1 2 3)") 
 6 $ clj -M:cljs - m cljs.main - re node 
 ClojureScript 1.11.54 cljs.user = > (require '[sci.core :as sci]) nil cljs.user = > (sci/eval - string "(+ 1 2 3)") 
 6

Slide 7

Slide 7 text

Nbb (Node.js) Scittle (browser) Joyride (VSCode) 4ever-clojure Clerk viewers Maria.cloud (soon) More... Small Clojure Interpreter NEW USAGES OF CLJS THROUGH SCI

Slide 8

Slide 8 text

CLJS Scripting on Node.js $ npx nbb user = > (require '["child_process" :as cp]) nil user = > (str (cp/execSync "echo dude")) "dude\n" user = > (require '[clojure.math :as math]) 
 nil 
 user = > (math/IEEE-remainder 10 math/PI) 0.5752220392306207

Slide 9

Slide 9 text

Promesa

Slide 10

Slide 10 text

Nbb-compatible CLJS web framework Sitefox

Slide 11

Slide 11 text

AWS Lambda, now with first class parentheses Deploying Clojure on AWS Lambda with no compromises Ray McDermott rmc@juxt.pro

Slide 12

Slide 12 text

https://www.loop-code-recur.io/simple-site-analytics-with-serverless-clojure/ Serverless site analytics with Clojure nbb and AWS

Slide 13

Slide 13 text

See https://github.com/nextjournal/clerk/blob/main/ui_tests/playwright_tests.cljs Playwright (UI Tests)

Slide 14

Slide 14 text

Bun (Node.js alternative ) + nbb

Slide 15

Slide 15 text

https://youtu.be/7DQ0ymojfLg

Slide 16

Slide 16 text

Scittle Eval ClojureScript (SCI) directly in browser tags!

Slide 17

Slide 17 text

Scittle Load ClojureScript directly from GitHub source 
 https://babashka.org/scittle/wordle.html

Slide 18

Slide 18 text

Scittle

Slide 19

Slide 19 text

https://codepen.io/ordnungswidrig/pen/xxpPYNO

Slide 20

Slide 20 text

Scittle on Apropos https://vimeo.com/754492531

Slide 21

Slide 21 text

Joyride

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

Joyride: NPM libaries

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

🪶 Light-weight (memory) ⚡ Fast to start 
 👩🔧 Easy to install & operate 🔋 Batteries included What about a small(er) CLJS compiler?

Slide 26

Slide 26 text

Cherry 🍒 • ClojureScript (immutable data structures, cljs.core lib) • Bundle size starts at 300kb / 56kb gzipped Squint 🤤 • CLJS syntax but compiles directly to JS: • No immutability, just JS objects all the way down • Bundle size starts at 5 bytes? :) Both projects share common compiler code (fork of scriptjure + cherry-picked code from CLJS)

Slide 27

Slide 27 text

Cherry • Compile .cljs fi les on the fl y into ES6-compatible .js fi les. • npm install cherry-cljs + available on JVM (.cljc) • CLJS core functions shared across builds in common lib • Enables distributing compiled CLJS on NPM • No Google Closure (light weight, compatible with everyday JS tools) • Macro support • REPL support • Async/await • Native JS destructuring • Native JSX support 🍒

Slide 28

Slide 28 text

$ npm install cherry - cljs 
 $ cat example.cljs (ns example) (defn foo [x] (assoc x :foo true)) (prn (foo {})) 
 
 $ npx cherry run example.cljs [cherry] Running example.cljs {:foo true} 
 
 $ cat example.mjs import { assoc, keyword, prn, arrayMap } from 'cherry - cljs/cljs.core.js' var foo = function (x) { return assoc.call(null, x, keyword("foo"), true); }; prn.call(null, foo.call(null, arrayMap())); Cherry .cljs → .mjs

Slide 29

Slide 29 text

$ npm install esbuild 
 $ npx esbuild example.mjs - - bundle - - minify - - platform=node - - outf i le=foo.mjs - - format=esm foo.mjs 296.3kb ⚡ Done in 36ms 
 $ time node foo.mjs {:foo true} node foo.mjs 0.05s user 0.01s system 94% cpu 0.065 total Cherry + JS Toolchain

Slide 30

Slide 30 text

$ cat example.cljs 
 (ns example) (defn ^:async fetch - clojure [] (let [response (js/await (js/fetch "https: / / clojure.org"))] (.text response))) (prn (.substring (js/await (fetch - clojure)) 0 150)) $ node - v v18.6.0 
 
 $ npx cherry run example.cljs [cherry] Running example.cljs "\n < ! - - This site was created in Web f l ow. http: / / w w w .web f l ow.com - - > \n < ! - - Last Published: Fri Nov 13 2015 01 : 48 : 45 GMT+0000 (UTC) - - > \n

Slide 31

Slide 31 text

Cherry + JSX

Slide 32

Slide 32 text

Next.js, Vite, SolidJS, … 
 Hot-reloading, bundling: driven by JS tooling Smallest bundle: 300kb (56kb zipped)

Slide 33

Slide 33 text

Bundling cherry code Why do we have a baseline of 300kb / 56kb gzipped? • cherry-cljs/core.cljs is produced using Google Closure advanced compilation • All of ClojureScript is included • Google Closure does not emit code that can be optimized by ES6 treeshakers like esbuild, webpack • Everything is wrapped in one global object, instead of ES6-idiomatic code • Open issue: https://github.com/squint-cljs/cherry/issues/24

Slide 34

Slide 34 text

Vanilla and ES6-friendly JS output Bene fi ts: • Smaller outputs • More direct JS interop (back and forth) Downsides: • Giving up on persistent/immutable CLJS data structures 🤤 Squint

Slide 35

Slide 35 text

Squint + SolidJS: 5kb

Slide 36

Slide 36 text

$ npm install squint - cljs 
 $ cat example.cljs (ns example) (defn foo [x] (assoc x :foo true)) (prn (foo {})) 
 
 $ npx squint run example.cljs - - extension mjs [squint] Running example.cljs {"foo":true} 
 
 $ cat example.mjs import { assoc, prn } from 'squint - cljs/core.js' var foo = function (x) { return assoc(x, "foo", true); } ; prn(foo(({ }))); export { foo } Squint .cljs → .mjs

Slide 37

Slide 37 text

$ npm install esbuild 
 $ npx esbuild example.mjs - - bundle - - minify - - platform=node - - outf i le=foo.mjs - - format=esm foo.mjs 1.4kb ⚡ Done in 4ms 
 $ time node foo.mjs {"foo":true} node foo.mjs 0.04s user 0.01s system 90% cpu 0.057 total Squint + JS Toolchain

Slide 38

Slide 38 text

Squint Core Lib: Shallow Immutability Will Acton @lilactown Cora Sutton @corasaurus_hex

Slide 39

Slide 39 text

Squint Core Lib: Laziness via generators

Slide 40

Slide 40 text

Browser, Node, JVM / babashka Supported Environments

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

REPL + ES6 REPL = compile + eval Eval in ES6 context = dynamic import Dynamic import returns module, not a value - what to print? Special REPL mode in compiler • De fi nes global namespace state • De fi nes global return value

Slide 43

Slide 43 text

Future work • Unify common compiler code into one repo • Better (n)REPL support • Better macro support • Optimizations • Contribute back to CLJS?

Slide 44

Slide 44 text

CLJS reimagined? • Support for ES6 modules • Async/await • Compiling ad-hoc / small snippets (scripting, on the fl y compilation) • (Also) run without JVM • JSX • Distributing compiled CLJS libraries on NPM for usage from JS • Interop improvements: JS destructuring

Slide 45

Slide 45 text

Thanks to sponsors! https://opencollective.com/babashka/contribute https://github.com/sponsors/borkdude 🙏

Slide 46

Slide 46 text

Thanks! Michiel Borkent @borkdude Dutch Clojure Days 2022