Fork me on GitHub
#squint
<
2023-10-04
>
tatut10:10:58

can I use squint as a Clojure (JVM) library to translate form to js snippet?

borkdude11:10:02

(require '[squint.compiler :as squint])
(squint/compile-string ...)

tatut11:10:52

very nice, thanks

tatut11:10:24

so one could make a macro that stringifies a form and compiles it

borkdude11:10:22

whatever you want. note that the output is ES6. you can check the source of https://squint-cljs.github.io/squint/ how to import that in your page

tatut11:10:01

oh right, that sort of import clauses wouldn’t be suitable for onclick handlers or the like

borkdude11:10:41

not really no, but you can elide the import and export stuff, but if something is used from squint's standard library, your on your own

borkdude11:10:10

it is possible to configure the squint stdlib prefix, so if you would import it yourself and define it as a global, that could work

👍 1
tatut11:10:51

I’ll try to play around with it

borkdude11:10:22

ok let me know how it goes

jeroenvandijk11:10:42

I came to a similar conclusion regarding the onClick handler. What you can do is have a macro (in clojure) like below and have an evalSquint function in javascript. There are probably better and more efficient ways than this idea

(defmacro js [& body]
  (let [body (list (list (apply list 'fn '[_] body)))]    
    `(str 
      "evalSquint('" (pr-str (quote ~@body)) "')")))
Somewhere in the html (hiccup here):
[:script {:async true :src ""}]
    [:script {:type "importmap"}
     (json/generate {"imports" {"squint-cljs" "",
                                "squint-cljs/core.js" "",
                                "squint-cljs/string.js" ""}})]

    [:script {:type "module"}
     "import { compileString } from 'squint-cljs'
      var counter = 0;
      window.evalSquint = (code) => {
//console.log(\"code\", code);
        counter = counter + 1;
let js = compileString(code)
// console.log(\"squint-compile\",js)
const encodedJs = encodeURIComponent(js);
        const dataUri = 'data:text/javascript;charset=utf-8;eval=' + counter + ',' + encodedJs;
 import(dataUri);

      }"]

tatut11:10:10

that means you are doing the cljs->js translation in the client side

jeroenvandijk11:10:38

Yes, because I didn't find a way to deal with the modules

tatut11:10:48

wouldn’t you want to do it at compile time in the backend

borkdude11:10:04

you can get rid of the module stuff, let me hack something together

borkdude11:10:04

user=> (squint.compiler/compile-string* "(map inc [1 2 3])")
{:imports "import * as squint_core from 'squint-cljs/core.js';\n", :exports "", :body "squint_core.map(squint_core.inc, [1, 2, 3]);\n", :javascript "import * as squint_core from 'squint-cljs/core.js';\nsquint_core.map(squint_core.inc, [1, 2, 3]);\n", :jsx false, :ns user}

borkdude11:10:29

so if you just take :body then you can directly use this, provided that you define squint_core yourself somewhere

tatut11:10:37

that looks good

borkdude11:10:12

but one of the issues is that squint/core.js is also ES6, I could do an UMD build of that perhaps

borkdude12:10:20

This works:


<html>
  <head>
    <title>Squint</title>
    <script type="importmap">
      {
        "imports": {
          "squint-cljs": "",
          "squint-cljs/core.js": "",
          "squint-cljs/string.js": ""
        }
      }
    </script>
    <script type="module">
      import * as squint_core from 'squint-cljs/core.js';
      globalThis.squint_core = squint_core;
    </script>
    <script type="module">
      squint_core.prn(squint_core.map(squint_core.inc, [1, 2, 3]));
    </script>
  </head>
  <body>
    <button onClick="squint_core.prn(squint_core.map(squint_core.inc, [1, 2, 3]));">
      Click me
    </button>
  </body>
</html>

borkdude12:10:35

the onClick thing is generated by the compile-string* call

borkdude12:10:05

but note that script type="module" is evaluated asynchronously, so any code which uses squint but must evaluated after that

borkdude12:10:35

I used globalThis.squint_core = squint_core; as a hack to make squint core globally defined

borkdude12:10:59

feedback welcome!

jeroenvandijk12:10:56

Looks good to me! (I don't what I was doing things with the data uri's. Working with javascript modules is new to me)

tatut12:10:00

doesn’t look too hacky for me

borkdude12:10:47

yeah those data URIs are pretty gnarly to work with

tatut12:10:22

can you customize what the “squint_core” is called? like if I want it to be _sq to generate smaller snippets

borkdude12:10:05

yes:

user=> (:body (squint.compiler/compile-string* "(require '[squint.string :as str]) (prn (str/join \",\" (map inc [1 2 3])))" {:core-alias "squint.core"}))
"squint.core.prn(str.join(\",\", squint.core.map(squint.core.inc, [1, 2, 3])));\n"

borkdude12:10:11

The option is called :core-alias

borkdude12:10:43

I'm working on an UMD build of squint core now

borkdude12:10:53

this should make it easier, no need for async module stuff

tatut12:10:04

that was quick 🎉

borkdude12:10:24

This is now the entire "demo":


<html>
  <head>
    <title>Squint</title>
    <script src=""></script>
    <!-- rename squint.core to a shorter alias at your convenience: -->
    <script>globalThis._sc = squint.core;</script>
    <!-- compile JS on the server using: (squint.compiler/compile-string* "(prn (map inc [1 2 3]))" {:core-alias "_sc"}) -->
    <script>
      _sc.prn(_sc.map(_sc.inc, [1, 2, 3]));
    </script>
  </head>
  <body>
    <button onClick="_sc.prn(_sc.map(_sc.inc, [1, 2, 3]));">
      Click me
    </button>
  </body>
</html>

borkdude12:10:34

I eased the way in which squint can be compiled on a JVM server and then using the output in the browser: https://github.com/squint-cljs/squint#compile-on-a-server-use-in-a-browser

🎉 1