$30 off During Our Annual Pro Sale. View Details »

ClojureScript Reimagined - Dutch Clojure Days 2022

ClojureScript Reimagined - Dutch Clojure Days 2022

Michiel Borkent

October 30, 2022
Tweet

More Decks by Michiel Borkent

Other Decks in Programming

Transcript

  1. Michiel Borkent @borkdude Dutch Clojure Days 2022 ClojureScript Reimagined

  2. 🪶 Light-weight (memory) ⚡ Fast to start 
 👩🔧 Easy

    to install & operate (single binary) 🔋 Batteries included
  3. Can we fulfil new Clojure use cases with similar approaches?

    What about (CL)JS targets? JavaScript as universal glue language
  4. 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?
  5. 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
  6. 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
  7. Nbb (Node.js) Scittle (browser) Joyride (VSCode) 4ever-clojure Clerk viewers Maria.cloud

    (soon) More... Small Clojure Interpreter NEW USAGES OF CLJS THROUGH SCI
  8. 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
  9. Promesa

  10. Nbb-compatible CLJS web framework Sitefox

  11. AWS Lambda, now with first class parentheses Deploying Clojure on

    AWS Lambda with no compromises Ray McDermott rmc@juxt.pro
  12. https://www.loop-code-recur.io/simple-site-analytics-with-serverless-clojure/ Serverless site analytics with Clojure nbb and AWS

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

  14. Bun (Node.js alternative ) + nbb

  15. https://youtu.be/7DQ0ymojfLg

  16. Scittle Eval ClojureScript (SCI) directly in browser tags!

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

  18. Scittle

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

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

  21. Joyride

  22. None
  23. Joyride: NPM libaries

  24. None
  25. 🪶 Light-weight (memory) ⚡ Fast to start 
 👩🔧 Easy

    to install & operate 🔋 Batteries included What about a small(er) CLJS compiler?
  26. 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)
  27. 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 🍒
  28. $ 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
  29. $ 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
  30. $ 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 "<!DOCTYPE html>\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<htm" Cherry + async/await
  31. Cherry + JSX

  32. Next.js, Vite, SolidJS, … 
 Hot-reloading, bundling: driven by JS

    tooling Smallest bundle: 300kb (56kb zipped)
  33. 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
  34. 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
  35. Squint + SolidJS: 5kb

  36. $ 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
  37. $ 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
  38. Squint Core Lib: Shallow Immutability Will Acton @lilactown Cora Sutton

    @corasaurus_hex
  39. Squint Core Lib: Laziness via generators

  40. Browser, Node, JVM / babashka Supported Environments

  41. None
  42. 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
  43. Future work • Unify common compiler code into one repo

    • Better (n)REPL support • Better macro support • Optimizations • Contribute back to CLJS?
  44. 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
  45. Thanks to sponsors! https://opencollective.com/babashka/contribute https://github.com/sponsors/borkdude 🙏

  46. Thanks! Michiel Borkent @borkdude Dutch Clojure Days 2022