diff --git a/nbb.edn b/nbb.edn deleted file mode 100644 index 34890c0f..00000000 --- a/nbb.edn +++ /dev/null @@ -1 +0,0 @@ -{:deps {com.github.seancorfield/honeysql {:mvn/version "2.3.928"}}} diff --git a/script/nbb_tests.clj b/script/nbb_tests.clj index d76084ff..0a565d2c 100644 --- a/script/nbb_tests.clj +++ b/script/nbb_tests.clj @@ -99,7 +99,8 @@ (deftest classpath-test (testing "passing classpath cli arg" - (let [deps '{com.github.seancorfield/honeysql {:git/tag "v2.0.0-rc5" :git/sha "01c3a55"}} + (let [deps '{com.github.seancorfield/honeysql {:git/sha "23be700" + :git/tag "v2.3.928"}} _ (deps/add-deps {:deps deps}) cp (cp/get-classpath)] (is (= ["SELECT foo FROM bar WHERE baz = ?" 2] @@ -228,7 +229,9 @@ :exit))))) (deftest local-js-require-test - (is (= {:a 1, :other.script/foo 1} (nbb {:dir "test-scripts/local-js"} "-cp" "src" "-m" "script")))) + (is (= {:a 1, :other.script/foo 1 + :nss ["foo" "bar" "baz" "user"]} + (nbb {:dir "test-scripts/local-js"} "-cp" "src" "-m" "script")))) (defn parse-opts [opts] (let [[cmds opts] (split-with #(not (str/starts-with? % ":")) opts)] diff --git a/src/nbb/core.cljs b/src/nbb/core.cljs index 27f79d41..951c23eb 100644 --- a/src/nbb/core.cljs +++ b/src/nbb/core.cljs @@ -16,9 +16,7 @@ [sci.impl.vars :as vars] [sci.lang] [shadow.esm :as esm]) - (:require-macros [nbb.macros - :as macros - :refer [with-async-bindings]])) + (:require-macros [nbb.macros :as macros])) (set! *unrestricted* true) @@ -70,31 +68,32 @@ (def sci-find-ns (delay (sci/eval-form (store/get-ctx) 'find-ns))) -(defn load-module [m libname as refer rename libspecs] +(defn load-module [m libname as refer rename libspecs opts] (-> (if (some? (@sci-find-ns libname)) (js/Promise.resolve nil) (esm/dynamic-import m)) (.then (fn [_module] (let [nlib (normalize-libname libname)] - (when (and as nlib - (not= nlib libname)) - (sci/eval-form (store/get-ctx) - (list 'alias - (list 'quote libname) - (list 'quote nlib)))) - (let [libname (or nlib libname)] - (when as + (sci/binding [sci/ns (:ns opts)] + (when (and as nlib + (not= nlib libname)) (sci/eval-form (store/get-ctx) (list 'alias - (list 'quote as) - (list 'quote libname)))) - (when (seq refer) - (sci/eval-form (store/get-ctx) - (list 'clojure.core/refer (list 'quote libname) - :only (list 'quote refer) - :rename (list 'quote rename))))) - (handle-libspecs (next libspecs))))))) + (list 'quote nlib)))) + (let [libname (or nlib libname)] + (when as + (sci/eval-form (store/get-ctx) + (list 'alias + (list 'quote as) + (list 'quote libname)))) + (when (seq refer) + (sci/eval-form (store/get-ctx) + (list 'clojure.core/refer + (list 'quote libname) + :only (list 'quote refer) + :rename (list 'quote rename)))))) + (handle-libspecs (next libspecs) opts)))))) (def ^:private windows? (= "win32" js/process.platform)) @@ -169,7 +168,9 @@ ;; reagent.ratom => ./nbb_reagent.js ;; reagent.dom.server => "react" + "react-dom/server" + "./nbb_reagent_dom_server.js" -(defn ^:private handle-libspecs [libspecs] +(defn ^:private handle-libspecs [libspecs ns-opts] + ;; (assert (:ns ns-opts) "ns") + ;; (assert (:file ns-opts) "file") (if (seq libspecs) (let [fst (first libspecs) [libname & opts] (if (symbol? fst) @@ -182,19 +183,19 @@ refer (concat (:refer opts) (:refer-macros opts)) rename (:rename opts) munged (munge libname) - current-ns-str (str @sci/ns) + current-ns-str (str (:ns ns-opts)) current-ns (symbol current-ns-str)] (if as-alias (do (old-require fst) - (handle-libspecs (next libspecs))) + (handle-libspecs (next libspecs) ns-opts)) (case libname ;; built-ins (reagent.core) (do (load-react) - (load-module "./nbb_reagent.js" libname as refer rename libspecs)) + (load-module "./nbb_reagent.js" libname as refer rename libspecs ns-opts)) (reagent.ratom) - (load-module "./nbb_reagent.js" libname as refer rename libspecs) + (load-module "./nbb_reagent.js" libname as refer rename libspecs ns-opts) (reagent.dom.server) (do (load-react) @@ -210,7 +211,7 @@ ;; could make reagent directly use loaded-modules via a global so we ;; don't have to hardcode this. (set! ^js (.-nbb$internal$react-dom-server goog/global) mod)) - (load-module "./nbb_reagent_dom_server.js" libname as refer rename libspecs))) + (load-module "./nbb_reagent_dom_server.js" libname as refer rename libspecs ns-opts))) ;; (schema.core) ;; (load-module ((.-resolve (:require @ctx)) "@babashka/nbb-prismatic-schema/index.mjs") ;; libname as refer rename libspecs) @@ -219,11 +220,11 @@ ;; libname as refer rename libspecs) (let [feat (get feature-requires libname)] (cond - feat (load-module feat libname as refer rename libspecs) + feat (load-module feat libname as refer rename libspecs ns-opts) (string? libname) ;; TODO: parse properties (let [libname (if (str/starts-with? libname "./") - (path/resolve (path/dirname @sci/file) libname) + (path/resolve (path/dirname (:file ns-opts)) libname) libname) [libname properties*] (split-libname libname) munged (munge libname) @@ -251,7 +252,7 @@ (-> sci-ctx (sci/add-class! internal-subname mod-field) (sci/add-import! current-ns internal-subname field)))))) - (handle-libspecs (next libspecs))) + (handle-libspecs (next libspecs) ns-opts)) mod (js/Promise.resolve (-> (or @@ -271,29 +272,32 @@ ;; assume symbol (if (sci/eval-form (store/get-ctx) (list 'clojure.core/find-ns (list 'quote libname))) ;; built-in namespace - (do (old-require fst) - (handle-libspecs (next libspecs))) + (do (sci/binding [sci/ns (:ns ns-opts) + sci/file (:file ns-opts)] + (old-require fst)) + (handle-libspecs (next libspecs) ns-opts)) (if-let [the-file (find-file-on-classpath munged)] (-> (load-file the-file) (.then (fn [_] - (when as - (sci/eval-form (store/get-ctx) - (list 'clojure.core/alias - (list 'quote as) - (list 'quote libname)))) - (when (seq refer) - (sci/eval-form (store/get-ctx) - (list 'clojure.core/refer - (list 'quote libname) - :only (list 'quote refer) - :rename (list 'quote rename)))))) + (sci/binding [sci/ns (:ns ns-opts)] + (when as + (sci/eval-form (store/get-ctx) + (list 'clojure.core/alias + (list 'quote as) + (list 'quote libname)))) + (when (seq refer) + (sci/eval-form (store/get-ctx) + (list 'clojure.core/refer + (list 'quote libname) + :only (list 'quote refer) + :rename (list 'quote rename))))))) (.then (fn [_] - (handle-libspecs (next libspecs))))) + (handle-libspecs (next libspecs) ns-opts)))) (js/Promise.reject (js/Error. (str "Could not find namespace: " libname)))))))))) - (js/Promise.resolve @sci/ns))) + (js/Promise.resolve (:ns ns-opts)))) -(defn eval-ns-form [ns-form] +(defn eval-ns-form [ns-form opts] ;; the parsing is still very crude, we only support a subset of the ns form ;; and ignore everything but (:require clauses) (let [[_ns ns-name & ns-forms] ns-form @@ -305,16 +309,20 @@ ;; ignore all :require-macros for now other-forms (remove #(and (seq? %) (= :require-macros (first %))) other-forms) - ns-obj (sci/eval-form (store/get-ctx) (list 'do (list* 'ns ns-name other-forms) '*ns*)) + ns-obj (sci/binding [sci/ns @sci/ns] + (sci/eval-form (store/get-ctx) (list 'do (list* 'ns ns-name other-forms) '*ns*))) libspecs (mapcat rest - require-forms)] - (with-async-bindings {sci/ns ns-obj} - (handle-libspecs libspecs)))) + require-forms) + opts (assoc opts :ns ns-obj)] + (handle-libspecs libspecs opts))) (defn eval-require [require-form] (let [args (rest require-form) - libspecs (mapv #(sci/eval-form (store/get-ctx) %) args)] - (handle-libspecs libspecs))) + libspecs (mapv #(sci/eval-form (store/get-ctx) %) args) + sci-ns @sci/ns + sci-file @sci/file] + (handle-libspecs libspecs {:ns sci-ns + :file sci-file}))) (defn parse-next [reader] (sci/parse-next (store/get-ctx) reader @@ -334,12 +342,14 @@ (js/Promise.resolve nil) (rest form)) (= 'ns fst) - (.then (eval-ns-form form) + (.then (eval-ns-form form opts) (fn [ns-obj] - (eval-next ns-obj reader opts))) + (eval-next ns-obj reader (assoc opts :ns ns-obj)))) :else (try (let [pre-await await-counter - next-val (sci/eval-form (store/get-ctx) form) + next-val (sci/binding [sci/ns (:ns opts) + sci/file (:file opts)] + (sci/eval-form (store/get-ctx) form)) post-await await-counter] (if (= pre-await post-await) (.then (js/Promise.resolve nil) @@ -368,40 +378,43 @@ (defn eval-next "Evaluates top level forms asynchronously. Returns promise of last value." - ([prev-val reader] (eval-next prev-val reader nil)) - ([prev-val reader opts] - (let [next-val (try (if-let [parse-fn (:parse-fn opts)] - (parse-fn reader) - (parse-next reader)) - (catch :default e - (js/Promise.reject e)))] - (if (instance? js/Promise next-val) - next-val - (if (not= :sci.core/eof next-val) - (if (seq? next-val) - (eval-seq reader next-val opts) - (let [v (try (sci/eval-form (store/get-ctx) next-val) - (catch :default e - (->Reject e)))] - (if (instance? Reject v) - (js/Promise.reject (.-v v)) - (recur v reader opts)))) - ;; wrap normal value in promise - (js/Promise.resolve - (let [wrap (or (:wrap opts) - identity)] - (wrap prev-val)))))))) - -(defn eval-string* [s] - (with-async-bindings {sci/ns @sci/ns} - (let [reader (sci/reader s)] - (eval-next nil reader)))) + [prev-val reader opts] + (let [next-val (try (sci/binding [sci/ns (:ns opts)] + (if-let [parse-fn (:parse-fn opts)] + (parse-fn reader) + (parse-next reader))) + (catch :default e + (js/Promise.reject e)))] + (if (instance? js/Promise next-val) + next-val + (if (not= :sci.core/eof next-val) + (if (seq? next-val) + (eval-seq reader next-val opts) + (let [v (try + (sci/binding [sci/ns (:ns opts) + sci/file (:file opts)] + (sci/eval-form (store/get-ctx) next-val)) + (catch :default e + (->Reject e)))] + (if (instance? Reject v) + (js/Promise.reject (.-v v)) + (recur v reader opts)))) + ;; wrap normal value in promise + (js/Promise.resolve + (let [wrap (or (:wrap opts) + identity)] + (wrap prev-val {:ns (:ns opts)}))))))) + +(defn eval-string* [s opts] + (let [reader (sci/reader s)] + (eval-next nil reader opts))) (defn load-string "Asynchronously parses and evaluates string s. Returns promise." [s] - (with-async-bindings {warn-on-infer @warn-on-infer} - (eval-string* s))) + (let [sci-file @sci/file + sci-ns @sci/ns] + (eval-string* s {:ns sci-ns :file sci-file}))) (defn slurp "Asynchronously returns string from file f. Returns promise." @@ -417,9 +430,14 @@ (defn load-file [f] - (with-async-bindings {sci/file (path/resolve f)} + (let [sci-file (path/resolve f) + sci-ns @sci/ns] (-> (slurp f) - (.then load-string)))) + (.then #(sci/binding [sci/file sci-file + sci/ns sci-ns] + (load-string %))) + #_(.finally (fn [] + (prn :finally (str @sci/ns))))))) (defn register-plugin! [_plug-in-name sci-opts] (store/swap-ctx! sci/merge-opts sci-opts)) @@ -558,7 +576,9 @@ (swap! (:env (store/get-ctx)) assoc-in [:namespaces 'clojure.core 'require] - (fn [& args] (await (.then (handle-libspecs args) + (fn [& args] (await (.then (identity ;; with-async-bindings {sci/file @sci/file} + (handle-libspecs args {:ns @sci/ns + :file @sci/file})) (fn [_]))))) (def ^:dynamic *file* sci/file) ;; make clj-kondo+lsp happy diff --git a/src/nbb/impl/deps.cljs b/src/nbb/impl/deps.cljs index f038e0ce..bece9a41 100644 --- a/src/nbb/impl/deps.cljs +++ b/src/nbb/impl/deps.cljs @@ -10,14 +10,12 @@ (def default-nbb-cache-path ".nbb/.cache") - (defn hash-deps "Given a map of dependencies, generates a unique hash of that map for caching purposes." [deps] (.. (crypto/createHash "sha1") (update (str deps) "binary") (digest "hex"))) - (defn download-and-extract-deps! "Given a map of dependencies and a path, downloads all dependencies to '*nbb-path*/_deps/*hash-of-deps-map*/nbb-deps' and returns that full path." @@ -30,15 +28,15 @@ (when-not (fs/existsSync unzipped-path) (fs/mkdirSync deps-path #js {:recursive true}) (fs/writeFileSync deps-edn-path (str {:deps deps})) - (println "Downloading dependencies...") + (*print-err-fn* "Downloading dependencies...") (cproc/execSync (str "bb --config " deps-edn-path " uberjar " jar-path)) - (println "Extracting dependencies...") + (*print-err-fn* "Extracting dependencies...") (cproc/execSync (str "bb -e '(fs/unzip \"" jar-path "\" \"" unzipped-path "\")'")) - (println "Done.")) + (*print-err-fn* "Done.")) unzipped-path)) diff --git a/src/nbb/impl/nrepl_server.cljs b/src/nbb/impl/nrepl_server.cljs index 4b61d597..6f3386e2 100644 --- a/src/nbb/impl/nrepl_server.cljs +++ b/src/nbb/impl/nrepl_server.cljs @@ -6,12 +6,12 @@ ["path" :as path] [clojure.pprint :as pp] [clojure.string :as str] - [nbb.api :as api] - [nbb.classpath :as cp] [goog.string :as gstring] [goog.string.format] + [nbb.api :as api] + [nbb.classpath :as cp] [nbb.core :as nbb] - [nbb.impl.bencode :refer [encode decode-all]] + [nbb.impl.bencode :refer [decode-all encode]] [nbb.impl.repl-utils :as utils :refer [the-sci-ns]] [sci.core :as sci] [sci.ctx-store :as store]) @@ -102,10 +102,13 @@ (sci/alter-var-root sci/print-fn (constantly (fn [s] (send-fn request {"out" s})))) - (-> (nbb/eval-next nil (sci/reader code) {:wrap vector}) + (-> (nbb/eval-next nil (sci/reader code) {:ns ns + :file file + :wrap vector}) (.then (fn [v] - (let [v (first v)] - (reset! last-ns @sci/ns) + (let [[v opts] v + sci-ns (:ns opts)] + (reset! last-ns sci-ns) (sci/alter-var-root sci/*3 (constantly @sci/*2)) (sci/alter-var-root sci/*2 (constantly @sci/*1)) (sci/alter-var-root sci/*1 (constantly v)) @@ -113,7 +116,7 @@ (:nrepl.middleware.print/options request) v)] (send-fn request {"value" v - "ns" (str @sci/ns)}))) + "ns" (str sci-ns)}))) (send-fn request {"status" ["done"]}))) (.catch (fn [e] (sci/alter-var-root sci/*e (constantly e)) diff --git a/src/nbb/impl/repl.cljs b/src/nbb/impl/repl.cljs index 1aa0bf5c..36be5742 100644 --- a/src/nbb/impl/repl.cljs +++ b/src/nbb/impl/repl.cljs @@ -115,7 +115,9 @@ (-> (eval-expr socket #(nbb/eval-next nil nil - {:wrap vector + {:ns @last-ns + :file @sci/file + :wrap vector ;; TODO this is a huge workaround ;; we should instead re-organize the code in nbb.core :parse-fn (let [realized? (atom false)] @@ -126,8 +128,7 @@ the-val) :sci.core/eof)))})) (.then (fn [v] - (let [[val ns] - [(first v) (sci/eval-form (store/get-ctx) '*ns*)]] + (let [[val {:keys [ns]}] v] (reset! last-ns ns) (sci/alter-var-root sci/*3 (constantly @sci/*2)) (sci/alter-var-root sci/*2 (constantly @sci/*1)) diff --git a/test-scripts/load_file_test.cljs b/test-scripts/load_file_test.cljs index fcb7494a..dd9a4c3a 100644 --- a/test-scripts/load_file_test.cljs +++ b/test-scripts/load_file_test.cljs @@ -1,8 +1,7 @@ (ns load-file-test (:require [nbb.core :refer [load-file *file*]])) +(def f *file*) (.then (load-file "test-scripts/loaded_by_load_file_test.cljs") (fn [m] - (assoc m :load-file-test-file-dyn-var *file*))) - - + (assoc m :load-file-test-file-dyn-var f))) diff --git a/test-scripts/local-js/src/async_require.mjs b/test-scripts/local-js/src/async_require.mjs new file mode 100644 index 00000000..39c8686d --- /dev/null +++ b/test-scripts/local-js/src/async_require.mjs @@ -0,0 +1,15 @@ +import { loadString } from '../../../index.mjs'; + +var res1 = loadString("(ns foo) (str (ns-name *ns*))"); +var res2 = loadString("(ns bar) (str (ns-name *ns*))"); +var res3 = loadString("(ns baz) (str (ns-name *ns*))"); +var res4 = loadString("(str (ns-name *ns*))"); + +res1 = await res1; +res2 = await res2; +res3 = await res3; +res4 = await res4; + +// console.log(res1, res2, res3, res4); + +export { res1, res2, res3, res4 }; diff --git a/test-scripts/local-js/src/script.cljs b/test-scripts/local-js/src/script.cljs index af4828f7..b5d5bc0a 100644 --- a/test-scripts/local-js/src/script.cljs +++ b/test-scripts/local-js/src/script.cljs @@ -1,8 +1,16 @@ (ns script (:require + ["./async_require.mjs" :refer [res1 res2 res3 res4]] ["./foo.mjs$default" :as foo] - [other.script :as o])) + [nbb.core :refer [await]] + [promesa.core :as p])) + +(def p (require '[other.script :as o])) + +(await (p/delay 50)) +(await p) (defn -main [& _] (assoc (js->clj foo :keywordize-keys true) - :other.script/foo o/foo)) + :other.script/foo o/foo + :nss [res1 res2 res3 res4])) diff --git a/test/nbb/main_test.cljs b/test/nbb/main_test.cljs index e20387e0..2f8ebfb7 100644 --- a/test/nbb/main_test.cljs +++ b/test/nbb/main_test.cljs @@ -1,12 +1,14 @@ (ns nbb.main-test - (:require ["module" :refer [createRequire]] - ["path" :as path] - [clojure.string :as str] - [clojure.test :as t :refer [deftest is testing]] - [nbb.classpath :as cp] - [nbb.core :as nbb] - [nbb.impl.main :as main] - [nbb.test-test]) + (:require + ["module" :refer [createRequire]] + ["path" :as path] + [clojure.string :as str] + [clojure.test :as t :refer [deftest is testing]] + [nbb.classpath :as cp] + [nbb.core :as nbb] + [nbb.impl.main :as main] + [nbb.test-test] + [sci.core :as sci]) (:require-macros [nbb.test-macros :refer [deftest-async]])) (defmethod t/report [:cljs.test/default :begin-test-var] [m] @@ -78,13 +80,15 @@ (.then (fn [ns-name] (is (= 'foo ns-name)))) (.then (fn [_] (nbb/load-string - "(nbb.core/load-string \"(ns foo) (defn foo [] (+ 1 2 3)) (ns-name *ns*)\")"))) + "(nbb.core/load-string \"(ns bar) (defn foo [] (+ 1 2 3)) (ns-name *ns*)\")"))) (.then (fn [ns-name] (testing "internal load-string" - (is (= 'foo ns-name))))) + (is (= 'bar ns-name))))) (.then (fn [_] - (nbb/load-string "(ns-name *ns*)"))) + (sci/binding [sci/ns (sci/create-ns 'user)] + (nbb/load-string "(ns-name *ns*)")))) (.then (fn [ns-name] + (prn :nsss ns-name) (is (= 'user ns-name)))) (.then (fn [_] (nbb/load-file "test-scripts/script.cljs")))