Skip to content

squint-cljs/squint-inline

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

squint-inline

Write squint functions and inline expressions in a ClojureScript project.

Squint operates on JavaScript objects and arrays, so assoc, update-in and select-keys work on those without converting them with js->clj and clj->js.

Install

deps.edn:

io.github.squint-cljs/squint-inline {:git/sha "669af4a33086e87232bf16007d9ddefa43185c3f"}

The squint core library is a separate npm package. This library declares it in deps.cljs, so with shadow-cljs install it with:

clojure -M -m shadow.cljs.npm-deps

Vanilla ClojureScript installs it while compiling when the build sets :install-deps true. Adding it to package.json by hand works too:

"squint-cljs": "^0.14.206"

Usage

Require the squint core runtime and the squint-inline.core macro namespace:

(ns app.core
  (:require ["squint-cljs/core.js" :as squint-core]
            [squint-inline.core :as squint]))

Use squint/defn to define a function with a squint body:

(squint/defn public-user [resp]
  (-> resp
      (select-keys [:id :name])
      (assoc :active true)))

(public-user (js/JSON.parse "{\"id\":1,\"name\":\"ada\",\"secret\":\"x\"}"))
;; => #js {:id 1 :name "ada" :active true}

Use squint/inline for an expression. Referenced ClojureScript locals are passed in as arguments:

(let [role "admin"]
  (squint/inline (assoc (js/JSON.parse "{\"id\":1}") :role role)))
;; => #js {:id 1 :role "admin"}

Require JavaScript libraries in the ClojureScript namespace and use their alias in a squint body:

(ns app.core
  (:require ["squint-cljs/core.js" :as squint-core]
            ["date-fns" :as dfns]
            [squint-inline.core :as squint]))

(squint/defn overdue [rows now]
  (->> rows
       (filter (fn [row] (dfns/isBefore (dfns/parseISO (:due row)) now)))
       (map (fn [row]
              (assoc row :daysLate (dfns/differenceInDays now (dfns/parseISO (:due row))))))
       vec))

(overdue (js/JSON.parse "[{\"id\":1,\"due\":\"2026-01-10\"}]")
         (js/Date. "2026-01-20"))
;; => #js [#js {:id 1 :due "2026-01-10" :daysLate 10}]

Inside a squint body [1 2 3] is already a JS array and {:a 1} a JS object, so #js is optional.

A squint function can call another squint function, in the same namespace or through a namespace alias, and can pass it around as a value.

(squint/defn active? [row]
  (= "ok" (:status row)))

(squint/defn actives [rows]
  (vec (filter active? rows)))

Other ClojureScript vars are not available inside a body.

Property access in a squint body works under :advanced without manual ^js hints.

Bundle size

With shadow-cljs defaults the first squint body adds about 19 KB gzipped. Squint's core.js is minified but not tree-shaken, so all of it ships once any squint body is present. A second and a hundredth squint body add almost nothing on top.

To get it tree-shaken, have shadow-cljs emit real ESM imports and let a JS bundler do the bundling.

;; shadow-cljs.edn
{:builds {:app {:target :esm
                :js-options {:js-provider :import}
                :modules {:main {:exports {main app.core/-main}}}}}}
clojure -M -m shadow.cljs.devtools.cli release app
npx esbuild out/main.js --bundle --minify --format=esm --outfile=out/bundle.js

One squint/defn calling assoc then costs 1,048 bytes gzipped instead of 19,365, and unused core functions are gone. The bundle is an ES module and runs in the browser with <script type="module">. For a classic script tag, pass --format=iife --global-name=app instead.

:js-provider :external does not help. It resolves modules by string at runtime, so a bundler cannot drop anything.

Vanilla ClojureScript has no equivalent. :target :bundle emits require("squint-cljs/core.js") rather than a static import, so the bundler keeps every export and the same single function costs 19,522 bytes gzipped. The compiler emits require for every npm library with no option to change it. Use shadow-cljs if bundle size matters.

See examples/esm.

Examples

examples/shadow, examples/cljs and examples/esm run the same source under :advanced.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages