Skip to content

Commit 5edf6b5

Browse files
borkdudemk
andauthored
Support :js-libs + sci/add-js-lib! (#873)
Co-authored-by: Martin Kavalar <[email protected]>
1 parent f6b42a7 commit 5edf6b5

File tree

14 files changed

+682
-388
lines changed

14 files changed

+682
-388
lines changed

API.md

Lines changed: 314 additions & 267 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SCI is used in [babashka](https://github.com/babashka/babashka),
1212

1313
## Unreleased
1414

15+
- Add `sci/add-js-lib!` for adding js libraries including corresponding `:js-libs` init option
1516
- Speed up Java interop around 5x by caching method lookups
1617
- Allow destructucturing in CLJS `defmethod`
1718
- [#862](https://github.com/babashka/sci/issues/862): fix JS constructor from class in CLJS namespace

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,34 @@ In JS hosts, to allow interop with anything, use the following config:
433433
{:classes {'js goog/global :allow :all}}
434434
```
435435

436+
### JavaScript libraries
437+
438+
Adding support for JavaScript libraries is done via the `:js-libs` option:
439+
440+
```clojure
441+
(ns sci.examples.js-libs
442+
(:require ["fs" :as fs]
443+
[sci.core :as sci]))
444+
445+
(sci/eval-string "
446+
(require '[\"fs\" :as fs])
447+
(fs/existsSync \"README.md\")"
448+
{:js-libs {"fs" fs}})
449+
;;=> true
450+
```
451+
452+
Note that JavaScript libraries _must_ be required using a string library name.
453+
454+
[Property notation](You can read about that
455+
[here](https://clojurescript.org/news/2021-04-06-release#_library_property_namespaces).) is also supported:
456+
457+
``` clojure
458+
(require '["fs$readFileSync" :as slurp])
459+
(slurp "README.md" "utf-8")
460+
```
461+
462+
JavaScript libraries can be added to an existing SCI context using `sci/add-js-lib!`.
463+
436464
### State
437465

438466
SCI uses a context (internally implemented using an atom) to keep track of state

bb.edn

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
{:paths ["libsci/bb"]
2-
3-
:pods {clj-kondo/clj-kondo {:version "2022.02.09"}}
42
:deps {io.github.borkdude/quickdoc
53
#_{:local/root "/Users/borkdude/dev/quickdoc"}
64
{:git/url "https://github.com/borkdude/quickdoc"
7-
:git/sha "b290f60fd68380485b613c711d98bea172f2f221"}}
5+
:git/sha "32e726cd6d785d00e49d4e614a05f7436d3831c0"}}
86

97
:tasks
108
{test:jvm {:doc "Run CLJ tests with leiningen"

deps.edn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
clj-commons/conch {:mvn/version "0.9.2"}
1212
funcool/promesa {:mvn/version "8.0.450"}}}
1313
:shadow {:extra-deps {thheller/shadow-cljs {:mvn/version "2.19.4"}}}
14+
:cljs {:extra-deps {org.clojure/clojurescript {:mvn/version "1.11.54"}}}
1415
:clj-test-runner
1516
{:extra-deps {com.cognitect/test-runner
1617
{:git/url "https://github.com/cognitect-labs/test-runner"

doc/async.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ anywhere, as long as the result is visible as a top level value:
107107

108108
## Registering a JS library as a class
109109

110+
> NOTE: the below code can be replaced by using `sci/add-js-lib!` from SCI > 0.6.37. See [docs](../README.md#javascript-libraries).
111+
110112
``` clojure
111113
(ns example
112114
(:require

examples/sci/examples/js_libs.cljs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(ns sci.examples.js-libs
2+
(:require ["fs" :as fs]
3+
[sci.core :as sci]))
4+
5+
(sci/eval-string "
6+
(require '[\"fs\" :as fs])
7+
(fs/existsSync \"README.md\")"
8+
{:js-libs {"fs" fs}})
9+
;;=> true

scratch.cljs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(require '[sci.impl.load] :reload)
2+
(require '[sci.core :as sci] :reload)
3+
4+
(def ctx (sci/init {}))
5+
6+
(sci/add-js-lib! ctx "fs" js/fs)
7+
8+
(sci/eval-form ctx '(do (require '["fs" :as fs] '[clojure.string :as str])
9+
(first (str/split-lines (fs/readFileSync "README.md" "utf-8")))))

src/sci/async.cljs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,14 @@
2626
(apply load/load-lib ctx nil libname opts))
2727
(handle-libspecs ctx (rest libspecs)))
2828
(if-let [load-fn (:async-load-fn @env*)]
29-
(let [opts (apply hash-map opts)]
29+
(let [opts (apply hash-map opts)
30+
[libname* path] (if (string? libname)
31+
(load/lib+path libname)
32+
[libname])]
3033
(.then (js/Promise.resolve (load-fn {:ns (sci/ns-name @last-ns)
3134
:ctx ctx
32-
:libname libname
35+
:libname libname*
36+
:property-path path
3337
:opts opts}))
3438
(fn [res]
3539
(let [ctx (or (:ctx res) ctx)]

src/sci/core.cljc

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
[sci.impl.callstack :as cs]
1515
[sci.impl.interpreter :as i]
1616
[sci.impl.io :as sio]
17+
[sci.impl.load :as load]
1718
[sci.impl.macros :as macros]
1819
[sci.impl.namespaces :as namespaces]
1920
[sci.impl.opts :as opts]
@@ -144,7 +145,6 @@
144145

145146
;; REPL variables
146147

147-
148148
(macros/deftime
149149
(defmacro with-in-str
150150
"Evaluates body in a context in which sci's *in* is bound to a fresh
@@ -359,7 +359,7 @@
359359
(let [publics (if exclude (apply dissoc publics exclude) publics)]
360360
publics))
361361

362-
(defn- exclude-when-meta [publics-map meta-fn key-fn val-fn skip-keys ]
362+
(defn- exclude-when-meta [publics-map meta-fn key-fn val-fn skip-keys]
363363
(reduce (fn [ns-map [var-name var]]
364364
(if-let [m (meta-fn var)]
365365
(if (some m skip-keys)
@@ -371,14 +371,13 @@
371371

372372
(defn- meta-fn [opts]
373373
(cond (= :all opts) identity
374-
opts #(select-keys % opts)
374+
opts #(select-keys % opts)
375375
:else #(select-keys % [:arglists
376376
:no-doc
377377
:macro
378378
:doc
379379
:dynamic])))
380380

381-
382381
(macros/deftime
383382
(defmacro copy-ns
384383
"Returns map of names to SCI vars as a result of copying public
@@ -462,6 +461,13 @@
462461
[:no-doc :skip-wiki]))]
463462
`(-copy-ns ~publics-map ~sci-ns)))))))
464463

464+
(defn add-import!
465+
"Adds import of class named by `class-name` (a symbol) to namespace named by `ns-name` (a symbol) under alias `alias` (a symbol). Returns mutated context."
466+
[ctx ns-name class-name alias]
467+
;; This relies on an internal format of the context and may change at any time.
468+
(swap! (:env ctx) assoc-in [:namespaces ns-name :imports alias] class-name)
469+
ctx)
470+
465471
(defn add-class!
466472
"Adds class (JVM class or JS object) to `ctx` as `class-name` (a
467473
symbol). Returns mutated context."
@@ -474,13 +480,6 @@
474480
(assoc-in [:raw-classes class-name] class))))
475481
ctx))
476482

477-
(defn add-import!
478-
"Adds import of class named by `class-name` (a symbol) to namespace named by `ns-name` (a symbol) under alias `alias` (a symbol). Returns mutated context."
479-
[ctx ns-name class-name alias]
480-
;; This relies on an internal format of the context and may change at any time.
481-
(swap! (:env ctx) assoc-in [:namespaces ns-name :imports alias] class-name)
482-
ctx)
483-
484483
(defn add-namespace!
485484
"Adds namespace map `ns-map` named by the symbol `ns-name` to
486485
`ctx`. Returns mutated context."
@@ -520,4 +519,11 @@
520519
(defn resolve [ctx sym]
521520
(@utils/eval-resolve-state ctx {} sym))
522521

522+
#?(:cljs
523+
(defn add-js-lib!
524+
"Add js library to context, so it can be used with `require`."
525+
[ctx name-str js-lib]
526+
(swap! (:env ctx) assoc-in [:js-libs name-str] js-lib)
527+
ctx))
528+
523529
;;;; Scratch

0 commit comments

Comments
 (0)