Added virtually the whole of the Scittle distribution, as it proves to be

virtually necessary to do development.
This commit is contained in:
Simon Brooke 2025-09-01 12:49:14 +01:00
parent fcb023ecc5
commit 0a200aca94
741 changed files with 174111 additions and 11 deletions

View file

@ -0,0 +1,13 @@
(ns scittle.cljs-ajax
(:require [ajax.core]
[sci.core :as sci]
[scittle.core :as scittle]))
(def ans (sci/create-ns 'ajax.core nil))
(def ajax-namespace
(sci/copy-ns ajax.core ans))
(scittle/register-plugin!
::ajax
{:namespaces {'ajax.core ajax-namespace}})

View file

@ -0,0 +1,6 @@
(ns scittle.cljs-devtools
(:require [devtools.core :as devtools]))
(devtools/set-pref! :disable-advanced-mode-check true)
(devtools/install!)

143
src/scittle/core.cljs Normal file
View file

@ -0,0 +1,143 @@
(ns scittle.core
(:refer-clojure :exclude [time])
(:require [cljs.reader :refer [read-string]]
[goog.object :as gobject]
[goog.string :as gstring]
[goog.string.format]
[sci.core :as sci]
[sci.ctx-store :as store]
[sci.impl.unrestrict]
[scittle.impl.common :refer [cljns]]
[scittle.impl.error :as error]
[clojure.string :as str]))
(set! sci.impl.unrestrict/*unrestricted* true)
;; make document conditional
(def ^js doc js/globalThis.document)
(clojure.core/defmacro time
"Evaluates expr and prints the time it took. Returns the value of expr."
[expr]
`(let [start# (cljs.core/system-time)
ret# ~expr]
(prn (cljs.core/str "Elapsed time: "
(.toFixed (- (system-time) start#) 6)
" msecs"))
ret#))
(def stns (sci/create-ns 'sci.script-tag nil))
(def rns (sci/create-ns 'cljs.reader nil))
(def namespaces
{'clojure.core
{'time (sci/copy-var time cljns)
'system-time (sci/copy-var system-time cljns)
'random-uuid (sci/copy-var random-uuid cljns)
'read-string (sci/copy-var read-string cljns)
'update-keys (sci/copy-var update-keys cljns)
'update-vals (sci/copy-var update-vals cljns)
'parse-boolean (sci/copy-var parse-boolean cljns)
'parse-double (sci/copy-var parse-double cljns)
'parse-long (sci/copy-var parse-long cljns)
'parse-uuid (sci/copy-var parse-uuid cljns)
'NaN? (sci/copy-var NaN? cljns)
'infinite? (sci/copy-var infinite? cljns)
'iteration (sci/copy-var iteration cljns)
'abs (sci/copy-var abs cljns)}
'goog.object {'set gobject/set
'get gobject/get}
'goog.string {'format gstring/format
'htmlEscape gstring/htmlEscape}
'goog.string.format {} ;; For cljs compatibility
'sci.core {'stacktrace sci/stacktrace
'format-stacktrace sci/format-stacktrace}})
(defn load-fn [{:keys [ctx] :as opts}]
(when-let [lib (and (string? (:namespace opts))
(gobject/get js/globalThis (:namespace opts)))]
(sci/add-js-lib! ctx (:namespace opts) lib)))
(store/reset-ctx!
(sci/init {:namespaces namespaces
:classes {'js js/globalThis
:allow :all
'Math js/Math}
:ns-aliases {'clojure.pprint 'cljs.pprint}
:features #{:scittle :cljs}
:load-fn load-fn}))
(unchecked-set js/globalThis "import" (js/eval "(x) => import(x)"))
(def !last-ns (volatile! @sci/ns))
(defn- -eval-string [s]
(sci/binding [sci/ns @!last-ns]
(let [rdr (sci/reader s)]
(loop [res nil]
(let [form (sci/parse-next (store/get-ctx) rdr)]
(if (= :sci.core/eof form)
(do
(vreset! !last-ns @sci/ns)
res)
(recur (sci/eval-form (store/get-ctx) form))))))))
(defn ^:export eval-string [s]
(try (-eval-string s)
(catch :default e
(error/error-handler e (:src (store/get-ctx)))
(throw e))))
(defn register-plugin! [_plug-in-name sci-opts]
(store/swap-ctx! sci/merge-opts sci-opts))
(defn- eval-script-tags* [script-tags]
(when-let [tag (first script-tags)]
(if-let [src (.getAttribute tag "src")]
(let [req (js/XMLHttpRequest.)
_ (.open req "GET" src true)
_ (gobject/set req "onload"
(fn [] (this-as this
(let [response (gobject/get this "response")]
(gobject/set tag "scittle_id" src)
;; save source for error messages
(store/swap-ctx! assoc-in [:src src] response)
(sci/binding [sci/file src]
(eval-string response)))
(eval-script-tags* (rest script-tags)))))]
(.send req))
(if-let [text (not-empty (str/trim (gobject/get tag "textContent")))]
(let [scittle-id (str (gensym "scittle-tag-"))]
(gobject/set tag "scittle_id" scittle-id)
(store/swap-ctx! assoc-in [:src scittle-id] text)
(sci/binding [sci/file scittle-id]
(eval-string text))
(eval-script-tags* (rest script-tags)))
(eval-script-tags* (rest script-tags))))))
(defn ^:export eval-script-tags [& [script-tags]]
(let [script-tags (or script-tags
(.querySelectorAll
doc "script[type='application/x-scittle']"))
script-tags (if (or (coll? script-tags)
(aget script-tags "length"))
script-tags
[script-tags])]
(eval-script-tags* script-tags)))
(def auto-load-disabled? (volatile! false))
(defn ^:export disable-auto-eval
"By default, scittle evaluates script nodes on the DOMContentLoaded
event using the eval-script-tags function. This function disables
that behavior."
[]
(vreset! auto-load-disabled? true))
(when doc
(.addEventListener doc
"DOMContentLoaded"
(fn [] (when-not @auto-load-disabled? (eval-script-tags))), false))
(enable-console-print!)
(sci/alter-var-root sci/print-fn (constantly *print-fn*))

View file

@ -0,0 +1,4 @@
(ns scittle.impl.common
(:require [sci.core :as sci]))
(def cljns (sci/create-ns 'clojure.core nil))

View file

@ -0,0 +1,92 @@
(ns scittle.impl.error
(:refer-clojure :exclude [println])
(:require [clojure.string :as str]
[sci.core :as sci]))
(defn println [& strs]
(.error js/console (str/join " " strs)))
(defn ruler [title]
(println (apply str "----- " title " " (repeat (- 50 7 (count title)) \-))))
(defn split-stacktrace [stacktrace verbose?]
(if verbose? [stacktrace]
(let [stack-count (count stacktrace)]
(if (<= stack-count 10)
[stacktrace]
[(take 5 stacktrace)
(drop (- stack-count 5) stacktrace)]))))
(defn print-stacktrace
[stacktrace {:keys [:verbose?]}]
(let [stacktrace (sci/format-stacktrace stacktrace)
segments (split-stacktrace stacktrace verbose?)
[fst snd] segments]
(run! #(print % "\n") fst)
(when snd
(print "...\n")
(run! #(print % "\n") snd))))
(defn error-context [ex src-map]
(let [{:keys [:file :line :column]} (ex-data ex)]
(when (and file line)
(when-let [content (get src-map file)]
(let [matching-line (dec line)
start-line (max (- matching-line 4) 0)
end-line (+ matching-line 6)
[before after] (->>
(str/split-lines content)
(map-indexed list)
(drop start-line)
(take (- end-line start-line))
(split-at (inc (- matching-line start-line))))
snippet-lines (concat before
[[nil (str (str/join "" (repeat (dec column) " "))
(str "^--- " (ex-message ex)))]]
after)
indices (map first snippet-lines)
max-size (reduce max 0 (map (comp count str) indices))
snippet-lines (map (fn [[idx line]]
(if idx
(let [line-number (inc idx)]
(str (.padStart (str line-number) max-size "0") " " line))
(str (str/join (repeat (+ 2 max-size) " ")) line)))
snippet-lines)]
(str "\n" (str/join "\n" snippet-lines)))))))
(defn right-pad [s n]
(let [n (- n (count s))]
(str s (str/join (repeat n " ")))))
(defn error-handler [e src-map]
(let [d (ex-data e)
sci-error? (isa? (:type d) :sci/error)
stacktrace (sci/stacktrace e)]
(ruler "Scittle error")
(when-let [name (.-name e)]
(when-not (= "Error" name)
(println "Type: " name)))
(when-let [m (.-message e)]
(println (str "Message: " m)))
(when-let [d (ex-data (ex-cause e) #_(.getCause e))]
(println (str "Data: ")
(pr-str d)))
(let [{:keys [:file :line :column]} d]
(when line
(println (str "Location: "
(when file (str file ":"))
line ":" column""))))
(when-let [phase (:phase d)]
(println "Phase: " phase))
(when-let [ec (when sci-error?
(error-context e src-map))]
(ruler "Context")
(println ec))
(when sci-error?
(when-let
[st (let [st (with-out-str
(when stacktrace
(print-stacktrace stacktrace src-map)))]
(when-not (str/blank? st) st))]
(ruler "Stack trace")
(println st)))))

View file

@ -0,0 +1,8 @@
(ns scittle.js-interop
(:require
[sci.configs.applied-science.js-interop :as j]
[scittle.core :as scittle]))
(scittle/register-plugin!
::js-interop
j/config)

19
src/scittle/nrepl.cljs Normal file
View file

@ -0,0 +1,19 @@
(ns scittle.nrepl
(:require
[clojure.edn :as edn]
[sci.nrepl.server :as nrepl-server]))
(defn ws-url [host port path]
(str "ws://" host ":" port "/" path))
(when-let [ws-port (.-SCITTLE_NREPL_WEBSOCKET_PORT js/window)]
(set! (.-ws_nrepl js/window)
(new js/WebSocket (ws-url (.-hostname (.-location js/window)) ws-port "_nrepl"))))
(when-let [ws (nrepl-server/nrepl-websocket)]
(set! (.-onmessage ws)
(fn [event]
(nrepl-server/handle-nrepl-message (edn/read-string (.-data event)))))
(set! (.-onerror ws)
(fn [event]
(js/console.log event))))

8
src/scittle/pprint.cljs Normal file
View file

@ -0,0 +1,8 @@
(ns scittle.pprint
(:require
[sci.configs.cljs.pprint :refer [config]]
[scittle.core :as scittle]))
(scittle/register-plugin!
::pprint
config)

8
src/scittle/promesa.cljs Normal file
View file

@ -0,0 +1,8 @@
(ns scittle.promesa
(:require
[sci.configs.funcool.promesa :as p]
[scittle.core :as scittle]))
(scittle/register-plugin!
::promesa
p/config)

View file

@ -0,0 +1,8 @@
(ns scittle.re-frame
(:require
[sci.configs.re-frame.re-frame :as rf]
[scittle.core :as scittle]))
(scittle/register-plugin!
::re-frame
rf/config)

19
src/scittle/reagent.cljs Normal file
View file

@ -0,0 +1,19 @@
(ns scittle.reagent
(:require
[reagent.dom :as rdom]
[sci.configs.reagent.reagent :refer [reagent-debug-namespace
reagent-namespace reagent-ratom-namespace]]
[sci.core :as sci]
[scittle.core :as scittle]))
(def rdns (sci/create-ns 'reagent.dom nil))
(def reagent-dom-namespace
{'render (sci/copy-var rdom/render rdns)})
(scittle/register-plugin!
::reagent
{:namespaces {'reagent.core reagent-namespace
'reagent.dom reagent-dom-namespace
'reagent.ratom reagent-ratom-namespace
'reagent.debug reagent-debug-namespace}})

View file

@ -0,0 +1,8 @@
(ns scittle.replicant
(:require
[sci.configs.cjohansen.replicant :refer [config]]
[scittle.core :as scittle]))
(scittle/register-plugin!
::replicant
config)