From 637ef834221538db9ef8f92afd5390bb0f98cf7f Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 7 Nov 2024 20:32:51 +0100 Subject: [PATCH] Fix #343: support :reload for reloading CLJS namespaces and JS code (#369) --- .github/workflows/ci.yml | 7 +++-- CHANGELOG.md | 4 +++ src/nbb/core.cljs | 67 ++++++++++++++++++++++++++-------------- test-scripts/reload.cljs | 9 ++++++ test-scripts/reload.js | 3 ++ test/nbb/main_test.cljs | 8 +++++ 6 files changed, 72 insertions(+), 26 deletions(-) create mode 100644 test-scripts/reload.cljs create mode 100644 test-scripts/reload.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0502050c..d3210f96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 @@ -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: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a9420c0..a6995305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/nbb/core.cljs b/src/nbb/core.cljs index c58de7c4..3d7f7956 100644 --- a/src/nbb/core.cljs +++ b/src/nbb/core.cljs @@ -138,7 +138,11 @@ (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 @@ -146,11 +150,16 @@ ((.-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)))) @@ -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) @@ -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)] @@ -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] @@ -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 diff --git a/test-scripts/reload.cljs b/test-scripts/reload.cljs new file mode 100644 index 00000000..df384a58 --- /dev/null +++ b/test-scripts/reload.cljs @@ -0,0 +1,9 @@ +(ns reload + (:require ["./reload.js"] :reload)) + +(defonce my-atom (atom 0)) + +(swap! my-atom inc) + +(def x js/globalThis.x) + diff --git a/test-scripts/reload.js b/test-scripts/reload.js new file mode 100644 index 00000000..0fe13289 --- /dev/null +++ b/test-scripts/reload.js @@ -0,0 +1,3 @@ +var x = globalThis.x || 0; +globalThis.x = x + 1; + diff --git a/test/nbb/main_test.cljs b/test/nbb/main_test.cljs index 82764268..4af68c71 100644 --- a/test/nbb/main_test.cljs +++ b/test/nbb/main_test.cljs @@ -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))