Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #343: support :reload for reloading CLJS namespaces and JS code #369

Merged
merged 20 commits into from
Nov 7, 2024
Merged
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ on: [push, pull_request]

jobs:
build:

runs-on: windows-latest

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

steps:
- name: Git checkout
uses: actions/checkout@v4
Expand Down Expand Up @@ -34,8 +37,8 @@ jobs:
cli: latest
bb: latest

- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
# - name: Setup tmate session
# uses: mxschmitt/action-tmate@v3

- name: Run tests
run: |
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ For a list of breaking changes, check [here](#breaking-changes).

[Nbb](https://github.com/babashka/nbb): Scripting in Clojure on Node.js using [SCI](https://github.com/babashka/sci)

## 1.3.195 (2024-11-07)

- [#343](https://github.com/babashka/nbb/issues/343): support `:reload` for reloading CLJS namespaces and JS code

## 1.3.194 (2024-10-23)

- Fix issue with loading `cljs.spec.alpha` by upgrading shadow-cljs
Expand Down
67 changes: 43 additions & 24 deletions src/nbb/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -138,19 +138,28 @@
(defn register-module [mod internal-name]
(swap! loaded-modules assoc internal-name mod))

(defn load-js-module [libname internal-name]
(defn debug [& xs]
(binding [*print-fn* *print-err-fn*]
(apply prn xs)))

(defn load-js-module [libname internal-name reload?]
(-> (if-let [resolve (:resolve @ctx)]
(-> (resolve libname)
(.catch
(fn [_]
((.-resolve (:require @ctx)) libname))))
(js/Promise.resolve ((.-resolve (:require @ctx)) libname)))
(.then (fn [path]
(esm/dynamic-import
(let [path (if (and windows? (fs/existsSync path))
(str (url/pathToFileURL path))
path)]
path))))
(let [file-url (if (str/starts-with? (str path) "file:")
path
(when (and (or windows? reload?) (fs/existsSync path))
(str (url/pathToFileURL path))))
path (if (and reload?
;; not "node:fs" etc
file-url)
(str file-url "?uuid=" (random-uuid))
(or file-url path))]
(esm/dynamic-import path))))
(.then (fn [mod]
(register-module mod internal-name)
mod))))
Expand Down Expand Up @@ -237,7 +246,8 @@
feat (load-module feat libname as refer rename libspecs ns-opts)
(string? libname)
(let [libname (if (str/starts-with? libname "./")
(path/resolve (path/dirname (:file ns-opts)) libname)
(path/resolve (path/dirname (or (:file ns-opts) "."))
libname)
libname)
[libname properties*] (split-libname libname)
munged (munge libname)
Expand Down Expand Up @@ -266,24 +276,27 @@
(sci/add-class! internal-subname mod-field)
(sci/add-import! current-ns internal-subname field))))))
(handle-libspecs (next libspecs) ns-opts))
mod (js/Promise.resolve
(->
(or
;; skip loading if module was already loaded
(some-> (get @loaded-modules internal-name)
js/Promise.resolve)
(load-js-module libname internal-name)
;; else load module and register in loaded-modules under internal-name
)
(.then (fn [mod]
(if properties
(gobj/getValueByKeys mod properties)
mod)))))]
mod (let [reload? (contains? (:opts ns-opts) :reload)]
(js/Promise.resolve
(->
(or
;; skip loading if module was already loaded
(and (not reload?)
(some-> (get @loaded-modules internal-name)
js/Promise.resolve))
(load-js-module libname internal-name reload?)
;; else load module and register in loaded-modules under internal-name
)
(.then (fn [mod]
(if properties
(gobj/getValueByKeys mod properties)
mod))))))]
(-> mod
(.then after-load)))
:else
;; assume symbol
(if (sci/eval-form (ctx/get-ctx) (list 'clojure.core/find-ns (list 'quote libname)))
(if (and (not (contains? (:opts ns-opts) :reload))
(sci/eval-form (ctx/get-ctx) (list 'clojure.core/find-ns (list 'quote libname))))
;; built-in namespace
(do (sci/binding [sci/ns (:ns ns-opts)
sci/file (:file ns-opts)]
Expand Down Expand Up @@ -336,11 +349,14 @@
ns-obj (sci/binding [sci/ns @sci/ns]
(sci/eval-form (ctx/get-ctx) (list 'do (list* 'ns ns-name other-forms) '*ns*)))
libspecs (mapcat rest require-forms)
ns-opts (into #{} (filter keyword? libspecs))
libspecs (remove keyword? libspecs)
opts (assoc opts :ns ns-obj)]
(handle-libspecs libspecs opts)))
(handle-libspecs libspecs (assoc opts :opts ns-opts))))

(defn eval-require [require-form]
(let [args (rest require-form)
args (remove keyword? args)
libspecs (mapv #(sci/eval-form (ctx/get-ctx) %) args)
sci-ns @sci/ns
sci-file @sci/file]
Expand Down Expand Up @@ -681,8 +697,11 @@
(if *old-require*
(apply old-require args)
(await (.then (identity ;; with-async-bindings {sci/file @sci/file}
(handle-libspecs args {:ns @sci/ns
:file @sci/file}))
(let [opts (into #{} (filter keyword? args))
args (remove keyword? args)]
(handle-libspecs args {:ns @sci/ns
:file @sci/file
:opts opts})))
(fn [_]))))))

(def ^:dynamic *file* sci/file) ;; make clj-kondo+lsp happy
Expand Down
9 changes: 9 additions & 0 deletions test-scripts/reload.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(ns reload
(:require ["./reload.js"] :reload))

(defonce my-atom (atom 0))

(swap! my-atom inc)

(def x js/globalThis.x)

3 changes: 3 additions & 0 deletions test-scripts/reload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var x = globalThis.x || 0;
globalThis.x = x + 1;

8 changes: 8 additions & 0 deletions test/nbb/main_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,14 @@ result")
(fn [val]
(is (number? val))))))

(deftest-async reload-test
(is (.then (nbb/load-string "(require 'reload)
(require 'reload :reload)

[@reload/my-atom reload/x]")
(fn [val]
(is (= [2 2] val))))))

(defn init []
(t/run-tests 'nbb.main-test 'nbb.test-test))

Loading