Giter Site home page Giter Site logo

nbb's Introduction

Not babashka. Node.js babashka!?

Ad-hoc CLJS scripting on Node.js.

Try it out

Run npx nbb to run nbb on your own machine, or try it in a browser on Replit!

Goals and features

Nbb's main goal is to make it easy to get started with ad hoc CLJS scripting on Node.js.

Additional goals and features are:

  • Fast startup without relying on a custom version of Node.js.
  • Small artifact (current size is around 1.2MB).
  • First class macros.
  • Support building small TUI apps using Reagent.
  • Complement babashka with libraries from the Node.js ecosystem.

Community

Requirements

Nbb requires Node.js v14 or newer.

Additionally, in the case of downloading Clojure dependencies, it requires the installation of babashka.

How does this tool work?

CLJS code is evaluated through SCI, the same interpreter that powers babashka. Because SCI works with advanced compilation, the bundle size, especially when combined with other dependencies, is smaller than what you get with self-hosted CLJS. That makes startup faster. The trade-off is that execution is less performant and that only a subset of CLJS is available (e.g. no deftype, yet).

Usage

Install nbb from NPM:

$ npm install nbb -g

Omit -g for a local install.

Try out an expression:

$ nbb -e '(+ 1 2 3)'
6

And then install some other NPM libraries to use in the script. E.g. with the following package.json:

{
  "dependencies": {
    "csv-parse": "^5.3.0",
    "shelljs": "^0.8.5",
    "term-size": "^3.0.2",
    "zx": "^7.1.1"
  }
}

Create a script which uses the NPM libraries:

(ns example
  (:require ["csv-parse/sync" :as csv]
            ["fs" :as fs]
            ["path" :as path]
            ["shelljs$default" :as sh]
            ["term-size$default" :as term-size]
            ["zx" :refer [$]]
            ["zx$fs" :as zxfs]
            [nbb.core :refer [*file*]]))

(prn (path/resolve "."))

(prn (term-size))

(println (count (str (fs/readFileSync *file*))))

(prn (sh/ls "."))

(prn (csv/parse "foo,bar"))

(prn (zxfs/existsSync *file*))

($ #js ["ls"])

Call the script:

$ nbb script.cljs
"/private/tmp/test-script"
#js {:columns 216, :rows 47}
510
#js ["node_modules" "package-lock.json" "package.json" "script.cljs"]
#js [#js ["foo" "bar"]]
true
$ ls
node_modules
package-lock.json
package.json
script.cljs

What does $default mean?

The :default foo syntax is shadow-cljs only and not supported by vanilla CLJS (and nbb doesn't support it either). The $default syntax is a recent addition to CLJS and should work in shadow-cljs too: this is why nbb supports it too.

See here for more info on that syntax.

Nbb implements :require via dynamic import (import() in JS). This is why you need to add $default to imports when you want to import the default object from a module.

Macros

Nbb has first class support for macros: you can define them right inside your .cljs file, like you are used to from JVM Clojure. Consider the plet macro to make working with promises more palatable:

(defmacro plet
  [bindings & body]
  (let [binding-pairs (reverse (partition 2 bindings))
        body (cons 'do body)]
    (reduce (fn [body [sym expr]]
              (let [expr (list '.resolve 'js/Promise expr)]
                (list '.then expr (list 'clojure.core/fn (vector sym)
                                        body))))
            body
            binding-pairs)))

Using this macro we can make async code look more like sync code. Consider this puppeteer example:

(-> (.launch puppeteer)
      (.then (fn [browser]
               (-> (.newPage browser)
                   (.then (fn [page]
                            (-> (.goto page "https://clojure.org")
                                (.then #(.screenshot page #js{:path "screenshot.png"}))
                                (.catch #(js/console.log %))
                                (.then #(.close browser)))))))))

Using plet this becomes:

(plet [browser (.launch puppeteer)
       page (.newPage browser)
       _ (.goto page "https://clojure.org")
       _ (-> (.screenshot page #js{:path "screenshot.png"})
             (.catch #(js/console.log %)))]
      (.close browser))

See the puppeteer example for the full code.

Since v0.0.36, nbb includes promesa which is a library to deal with promises. The above plet macro is similar to promesa.core/let.

Startup time

$ time nbb -e '(+ 1 2 3)'
6
nbb -e '(+ 1 2 3)'   0.17s  user 0.02s system 109% cpu 0.168 total

The baseline startup time for a script is about 170ms seconds on my laptop. When invoked via npx this adds another 300ms or so, so for faster startup, either use a globally installed nbb or use $(npm bin)/nbb script.cljs to bypass npx.

Libraries

See API documentation with a list of built-in Clojure libraries.

Dependencies

NPM dependencies

All NPM libraries loaded by a script are resolved relative to that script. When using the Reagent module, React is resolved in the same way as any other NPM library.

Clojure dependencies

Note: this feature relies on the presence of the babashka bb executable in the system's PATH.

To load dependencies from the Clojure ecosystem, you can create an nbb.edn:

{:deps {com.github.seancorfield/honeysql {:mvn/version "2.2.868"}}}

Similar to node_modules, nbb will unpack these dependencies in an .nbb directory and will load them from there.

Classpath

To load .cljs files from local paths or dependencies, you can use the --classpath argument. The current dir is added to the classpath automatically. So if there is a file foo/bar.cljs relative to your current dir, then you can load it via (:require [foo.bar :as fb]). Note that nbb uses the same naming conventions for namespaces and directories as other Clojure tools: foo-bar in the namespace name becomes foo_bar in the directory name.

Current file

The name of the file that is currently being executed is available via nbb.core/*file* or on the metadata of vars:

(ns foo
  (:require [nbb.core :refer [*file*]]))

(prn *file*) ;; "/private/tmp/foo.cljs"

(defn f [])
(prn (:file (meta #'f))) ;; "/private/tmp/foo.cljs"

Reagent

Nbb includes reagent.core which will be lazily loaded when required. You can use this together with ink to create a TUI application:

$ npm install ink

ink-demo.cljs:

(ns ink-demo
  (:require ["ink" :refer [render Text]]
            [reagent.core :as r]))

(defonce state (r/atom 0))

(def count
  (js/setInterval
   #(if (< @state 10)
      (swap! state inc)
      (js/clearInterval count))
   500))

(defn hello []
  [:> Text {:color "green"} "Hello, world! " @state])

(render (r/as-element [hello]))

Working with promises

Promesa

Working with callbacks and promises can become tedious. Since nbb v0.0.36 the promesa.core namespace is included with the let and do! macros. An example:

(ns prom
  (:require [promesa.core :as p]))

(defn sleep [ms]
  (js/Promise.
   (fn [resolve _]
     (js/setTimeout resolve ms))))

(defn do-stuff
  []
  (p/do!
   (println "Doing stuff which takes a while")
   (sleep 1000)
   1))

(p/let [a (do-stuff)
        b (inc a)
        c (do-stuff)
        d (+ b c)]
  (prn d))
$ nbb prom.cljs
Doing stuff which takes a while
Doing stuff which takes a while
3

Also see API docs.

REPL

In the REPL it can be convenient to bind the resolved value of promises to a var. You can do that like this:

(defmacro defp [binding expr]
  `(-> ~expr (.then (fn [val]
                     (def ~binding val)))))

(defp browser (.launch puppeteer #js {:headless false}))
(defp page (.newPage browser))
(.goto page "https://clojure.org")

Cljs-bean

Since nbb v0.1.0 cljs-bean is available.

See the example for an example.

Js-interop

Since nbb v0.0.75 applied-science/js-interop is available:

(ns example
  (:require [applied-science.js-interop :as j]))

(def o (j/lit {:a 1 :b 2 :c {:d 1}}))

(prn (j/select-keys o [:a :b])) ;; #js {:a 1, :b 2}
(prn (j/get-in o [:c :d])) ;; 1

Most of this library is supported in nbb, except the following:

  • destructuring using :syms
  • property access using .-x notation. In nbb, you must use keywords.

See the example of what is currently supported.

Reader conditionals

Nbb supports the following reader conditional features: :org.babashka/nbb and :cljs in that order of priority:

#?(:org.babashka/nbb 1 :cljs 2) ;;=> 1
#?(:cljs 2 :org.babashka/nbb 1) ;;=> 2

Main function

It is possible to use the -main function as the software (script) start point when using the m parameter of nbb passing your software namespace.

(ns example)

(defn -main
  [& args]
  (prn "print in -main"))

Execute:

nbb -m example

Testing

See doc/testing.

REPL

Console REPL

To start a console REPL, simply run nbb.

Socket REPL

To start a socket server REPL, run:

$ nbb socket-repl :port 1337

REPL API

Nbb exposes the nbb.repl namespace to programmatically start a REPL. See API for more info. An example:

(ns example
  (:require [nbb.repl :as repl]
            [promesa.core :as p]))

(defn available-in-repl [] :yolo)

(p/do!
 (repl/repl)
 ;; type (available-in-repl) in the REPL and it will return :yolo
 (println "The end"))

The repl function returns a promise. The promesa.core/do! macro waits for the REPL to finish and after that "The end" is printed:

$ nbb example.cljs
example=> (available-in-repl)
:yolo
example=> The end

To launch a REPL from a Node.js script, you can use loadString or loadFile:

import { loadString } from 'nbb'
await loadString(`
(require '[nbb.repl :refer [repl]])
(repl)
`)
console.log('The end!')
$ node repl.mjs
user=> (+ 1 2 3)
6
user=> The end!

nREPL

The nREPL server probably still has rough edges. Please report issues here.

An nREPL server can be started with:

$ nbb nrepl-server :port 1337

After that you can connect using an nREPL client:

$ lein repl :connect 1337

and evaluate expressions.

Running nREPL in Docker container is supported with the optional :host argument.

$ nbb nrepl-server :port 1337 :host 0.0.0.0

Calva

In Calva connect to the REPL with:

  • Connect to a Running REPL Server not in Project > ClojureScript nREPL server

CIDER

Use cider-jack-in-cljs as usual to start the nbb nREPL server from within an nbb project

or start an nREPL server from the command line with

$ nbb nrepl-server

and use cider-connect-cljs with a ClojureScript REPL type of nbb to connect to it.

CIDER prior to v1.6.0, needs the following workaround.

See also this article by Benjamin Scherdtner.

Vim Iced

See this tweet.

nREPL API

You can programmatically start and stop an nREPL server through:

(require '[nbb.nrepl-server :as nrepl])
(nrepl/start-server! {:port 1337})
(nrepl/stop-server!)

In a JavaScript project you can do the above through:

import { loadString } from 'nbb'

globalThis.inspectMyProcess = () => {
  return {version: process.version};
}

await loadString(`

(require '[nbb.nrepl-server :as nrepl])
(nrepl/start-server! {:port 1337})

`)

If you calling this from a CommonJS module, you can use dynamic import:

async function nREPL() {
  const { loadString } = await import('nbb');
  await loadString(`
  (require '[nbb.nrepl-server :as nrepl])
  (nrepl/start-server! {:port 1337})
`);
}

nREPL();

And then you can connect with an nREPL client:

$ node scratch.mjs &
nREPL server started on port 1337 on host 127.0.0.1 - nrepl://127.0.0.1:1337

$ lein repl :connect 1337
Connecting to nREPL at 127.0.0.1:1337
user=> js/process.argv
#js ["/Users/borkdude/.nvm/versions/node/v17.8.0/bin/node" "/Users/borkdude/dev/nbb/scratch.mjs"]
user=> (js/inspectMyProcess)
#js {:version "v17.8.0"}

Projects using nbb

The following projects are using nbb or are supporting it as a development platform:

Examples

Calling nbb from JavaScript

You can load nbb from JavaScript. Exposed functions are loadFile, loadString, addClassPath, getClassPath and printErrorReport.

An example:

Clojure:

(ns example)

(defn foo [] "Hello")

;; this JS object is the return value of loadFile:
#js {:foo foo}

JavaScript:

import { loadFile } from 'nbb'

// destructure JS object returned from .cljs file:
const { foo } = await loadFile('example.cljs')

// execute the foo function
foo();

Printing errors

Here's an example of how to print errors from the JS API:

import { loadString, printErrorReport } from 'nbb'

try {
  await loadString(`(assoc :foo :bar)`)
}
catch (e) {
  printErrorReport(e);
  process.exit(1);
}

Videos

Articles

Podcasts

Migrating to shadow-cljs

See this gist on how to convert an nbb script or project to shadow-cljs.

Publishing an nbb project to npm

See Publishing an nbb project to npm.

Creating a standalone executable with caxa

See Creating a standalone executable with caxa.

Nbb on AWS Lambda

See Nbb on AWS Lambda.

Nbb on Google Cloud Functions

See Nbb on Google Cloud Functions.

Nbb on fly.io

See Deploying an nbb app to fly.io.

Build

Prequisites:

  • babashka >= 0.4.0
  • Clojure CLI >= 1.10.3.933
  • Node.js 16.5.0 (lower version may work, but this is the one I used to build)

To build:

  • Clone and cd into this repo
  • bb release

Run bb tasks for more project-related tasks.

Credits

  • Original babashka logo by Nikita Prokopov. Node.js modifications by MnRa.

License

Copyright © 2021-2022 Michiel Borkent

Distributed under the EPL License. See LICENSE.

nbb's People

Contributors

avelino avatar benjamin-asdf avatar bobbicodes avatar bolivier avatar borkdude avatar darky avatar dataventis avatar dehli avatar dmg46664 avatar eval avatar ikappaki avatar jaidetree avatar lilactown avatar littleli avatar logseq-cldwalker avatar marcelocra avatar mauricioszabo avatar mnra avatar mraveloarinjaka avatar nbrandaleone avatar omariosouto avatar openefit avatar pez avatar prestancedesign avatar sher avatar sirwobin avatar vaclavsynacek avatar vharmain avatar zane avatar zoren avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nbb's Issues

Support server side rendering with reagent.dom.server

A pattern I am using in cljs web servers is to use Reagent's render-to-static-markup and inject the result into my template to be returned:

(:require [reagent.dom.server :refer [render-to-static-markup] :rename {render-to-static-markup r}])

;...

(aset template "innerHTML" (r [:div "My response]))

(where template here is an instance of node-html-parser on the server side)

This currently throws and error in nbb:

$ npx nbb myserver.cljs

> @ serve /home/chrism/dev/sitefox/examples/nbb
> nbb --classpath ../../src/ webserver.clj

----- Error --------------------------------------
Message:  No namespace: reagent.dom.server

Would be great to have access to reagent.dom.server!

Investigate loading of zx

(require '[promesa.core :as p]
         '["url" :as url])

;; (prn (url/pathToFileURL (.resolve js/require "zx/index.mjs")))
(js/import (doto (str (url/pathToFileURL (.resolve js/require "zx/index.mjs")))
             prn))
$ nbb zx.cljs
"file:///private/tmp/zx/node_modules/zx/index.mjs"
Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only file and data URLs are supported by the default ESM loader

Instantiating a js class throws an error

Steps to reproduce:

npm i keyv
(ns t
  (:require ["keyv" :as Keyv]))
(js/console.log Keyv)
(Keyv. "sqlite://./database.sqlite")

Result:

$ npx nbb test.cljs
[class Keyv extends EventEmitter]
#error {:message "Bind must be called on a function", :data {:type :sci/error, :line 4, :column 2, :message "Bind must be called on a function", :sci.impl/callstack #object[cljs.core.Delay {:status :pending, :val nil}], :file nil, :locals {}}, :cause #object[TypeError TypeError: Bind must be called on a function]}

Basic stdin/stdout REPL

We should support a basic stdin/stdout REPL.

Very rudimentary one in user space:

$ nbb -e '(ns user (:require ["readline" :as readline])) (defn repl [] (let [rl (.createInterface readline #js {:input js/process.stdin :output js/process.stdout})] (.question rl (str *ns* "> ") (fn [answer] (prn (try (load-string answer) (catch :default e e))) (.close rl) (repl))))) (repl)'

Also see https://github.com/anmonteiro/lumo/blob/master/src/js/repl.js

reagent / ink loading order problem

In the following example ink and react are loaded before reagent. In the reagent fork, the module is undefined, perhaps because it was already loaded?

The error: Cannot read property 'Component' of undefined.

(ns reagent
  (:require ["ink" :refer [render Text]]
            ["react" :as react]
            [reagent.core :as r]))

(defn example []
  (let [[count set-count] (react/useState 0)]
    (react/useEffect (fn []
                       (let [timer (js/setInterval #(set-count (inc count)) 500)]
                         (fn []
                           (js/clearInterval timer)))))
    [:> Text {:color "green"} "Hello, world! " count]))

(defn root []
  [:f> example])

(render (r/as-element [root]))

Windows CI

v0.0.70 has a fix for Windows, but we should test this in CI

Print a ”standard” message when the nREPL server is started

Is your feature request related to a problem? Please describe.

In adding nbb Jack-in to Calva the current message format when nbb has started its nREPL server tripped me a bit. It looks like so:

nRepl server started on port %d . nrepl-cljs-sci version %s 1337 TODO

So clearly it is just temporary and because the nREPL server is totally new.

Describe the solution you'd like

When babashka starts it prints a message like so:

Started nREPL server at 127.0.0.1:1667

Which Calva is prepared for and from where it picks up the host and port.

When the ”regular” Clojure nREPL server is started it prints:

nREPL server started on port 54836 on host localhost - nrepl://localhost:54836

Which Calva also is prepared for.

I know this is not really an API, but Calva certainly uses it as if it were. It would be nice if both nbb and babashka followed the nREPL pattern.

But since Calva already is adapted to babashka's format, I would be fine with nbb using that as well. I just wish that nbb will not invent some new format here.

Errors report "success" with exit code `0`.

version

$ which node; node --version
/usr/bin/node
v13.14.0
$ npm list -g | grep nbb
├── [email protected]

platform

Ubuntu 20.04 LTS with packaged Node.js v13.14.0.

problem

Scripting errors exit with status code 0 meaning "success."

repro

$ nbb -e '(print (* 6 7))'; echo "exit code: $?"
42
exit code: 0
$ nbb -e '(printt (* 6 7))'; echo "exit code: $?"
----- Error --------------------------------------
Message:  Could not resolve symbol: printt
Location: 1:2
Phase:    analysis
exit code: 0
$ 

expected behavior

Return anything but 0 as the exit code.

When errors return non-zero status code, nbb could be used as part of a larger script that checks its exit code.

Consider wrapper for setting NODE_PATH

Problem

Scripts can require libraries that are local to the script, not local to the nbb npm library itself.
To accomodate this I've used createRequire with the path of the script for requiring libs, but this doesn't work for built-in libs. E.g. the built-in reagent module which is lazy loaded on demand, only loads react from the nbb install itself, not from a local library.
But node has another workaround for this, which is called NODE_PATH which allows you to set another node_modules directory to be included and this would solve the above problem.

However, I don't like writing a .js script that wraps another instance of node. This will take another 50ms or so of startup time which is not optimal.

This could be solved using a simple bash / .cmd script which wraps node but these scripts would have to do the arg parsing to decide what the node_modules relative to the script is, which is very annoying in bash.

Possible solution

So I was thinking, maybe we could write a tiny nbb wrapper in golang, Rust or Zig (even smaller) which does the arg parsing for us and then calls the nbb runner with just the args parsed as JSON + sets the correct NODE_PATH env variable.
Then the 'real' nbb doesn't have to concern itself with arg parsing and will just receive the already JSON-parsed args in an entry point function.
If we write the wrapper in GraalVM we might be able to leverage gitlibs as well and resolve deps in the CLI rather than in node (see #20) and simply pass the entire classpath to the node part, which at that point, is just a list of plain directories. This saves us a bunch of JS coding while still being able to tap into the JS ecosystem.

Alternatives

  1. Hack

Hack: https://stackoverflow.com/a/60537950/6264

This hack seems to work, e.g. when you have a /tmp/foo/node_modules with shelljs and the below script lives in /tmp/start.cljs:

module.paths.unshift('/tmp/foo/node_modules');
process.env.NODE_PATH = process.env.NODE_PATH + ':/tmp/foo/node_modules';
module.constructor._initPaths();

require("shelljs");

If we can rely on this "hack" to work in the future, then this may be preferred as it's a much simpler workaround.
The question is if loading esm modules will be affected by this hack. If not, then we still would need the wrapper.

  1. Require nbb users to use babashka and use this wrapper script:
#!/usr/bin/env bb

(require '[babashka.fs :as fs]
         '[babashka.process :as p])

(def script (first *command-line-args*))

(-> (p/process (cond-> ["nbb"]
                 script (conj script))
               (cond-> {:inherit true}
                 script
                 (assoc-in [:extra-env "NODE_PATH"]
                           ;; TODO: preserve previous NODE_PATH if populated
                           (str (fs/path (fs/parent script) "node_modules")))))
    p/check)

nil
  1. Built-in support into babashka

Support -e

nbb -e '(+ 1 2 3)'

This already works by doing nbb /dev/stdin <<< '(prn (+ 1 2 3))' lol :)

ink: invalid hook call + reagent-cursor cannot be resolved

ink with its useInput-hook do not work for me. Since I was not sure (being a beginner in reagent) - i set up the same code without nbb and got it to work.

With nbb I get

  ERROR  Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
        1. You might have mismatching versions of React and the renderer (such as React DOM)
        2. You might be breaking the Rules of Hooks
        3. You might have more than one copy of React in the same app
        See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

I've put both code-bases into repos:


In the nbb-code it is also not possible to resolve r/cursor (which does work in the non-nbb variant)...

Add test suite

I've implemented a couple of async tests, but it's a pain in the neck to write tests that way.

Instead I'm going to write the tests in babashka and will just invoke the main JS on every test, testing the output of the process.

(js/import "module") doesn't work when using global ver of nbb

Just found out that (js/import "module") won't work with global nbb, but works if I've installed local nbb but running using global nbb.

For example this will work:

yarn global add nbb
yarn add nbb term-size
# uses (js/import "term-size") in script.cljs
nbb script.cljs

This will not work:

yarn global add nbb
yarn add term-size
# uses (js/import "term-size") in script.cljs
nbb script.cljs
# Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'term-size' imported from /Users/macair/.volta/tools/image/packages/nbb/lib/node_modules/nbb/out/nbb_core.js

Requiring *file* from nbb.core causes cljs requires to fail

Replicate:

a.cljs:

(ns a
  (:require
    ; [nbb.core :refer [*file*]]
    [b :refer [hi]]))
(hi)

b.cljs:

(ns b)

(defn hi []
  (print "hi"))

With the nbb.core require commented out:

$ npx nbb a.cljs 
hi

With the nbb.core require uncommented:

$ npx nbb a.cljs 
#error {:message "Could not resolve symbol: hi", :data {:type :sci/error, :line 6, :column 8, :file "/home/chrism/dev/c64core/no.cljs", :phase "analysis"}}

Support `:end-run-tests` multi-method for cljs.test

That's a good point. Normally clojure.test doesn't do anything with the exit code and you should inspect the test results to do that.

In the cljs.test docs about run-tests:

Runs all tests in the given namespaces; prints results.
Defaults to current namespace if none given. Does not return a meaningful
value due to the possiblity of asynchronous execution. To detect test
completion add a :end-run-tests method case to the cljs.test/report
multimethod.

So I think we should support this as the next step. I'll make an issue.

Originally posted by @borkdude in #41 (comment)

/cc @ErikSchierboom

Make library aliases work with fully qualified symbols

E.g.:

(ns foo.script
  (:require ["fs" :as fs]))
(println (count (str (fs/readFileSync "script.cljs"))))

Does normal CLJS create a "real" namespace for fs or is fs just the fs object from the library itself? And is fs/readFileSync then translated into an object lookup in this library, or is there a "real" namespace with a readFileSync function created behind the scenes?
It seems like it's doing something like the latter (with lumo):
cljs.user=> (str (fn [] (fs/readFileSync "script.cljs")))
"function (){\nreturn cljs.user.node$module$fs.readFileSync.call(null,"script.cljs");\n}"

Interesting bits from CLJS

Support or at least ignore cljs analyzer settings

In my specific case, I have a cljs program that does (set! *warn-on-infer* false) for a small section of the code that interacts tightly with node. nbb gives a "Could not resolve symbol: *warn-on-infer*" error and stops. If I comment out that setting it works with nbb. But on the flip-side normal cljs won't compile. Complicating this is that there is not nbb specific reader macro (or is there?) so I can't make the code support both. This happens during analysis so it can't be wrapped in a try/catch block.

Supporting the warnings probably is overkill for nbb, but perhaps those variables could at least be predefined so that those settings are a no-op. Or alternatively, perhaps undefined *warn-on-... variables could trigger a non-fatal warning instead of a stop-the-world error.

error in ubuntu/WSL2

$ nbb -e '(+ 1 2 3)'
/usr/local/lib/node_modules/nbb/out/nbb_main.js:3
import * as import$fs from "fs";
^

SyntaxError: Unexpected token *
at Module._compile (internal/modules/cjs/loader.js:723:23)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)

Set up CI for tests

This is the order I use locally, but we should do this in CI instead:

  • bb run-tests
  • bb release
  • bb run-integration-tests
  • npm publish

Perhaps only when pushing a new tag we should deploy.

Investigate :node-library target

From the shadows:

thheller as I said before from the build perspective this is all trivial. there just isn't a target that does this just yet for node. it could easily be built but for that I need more details about how you actually plan on doing any of this

12:10 PM
my suggestion is to build all of this completely on top of :node-library and then deal with the code splitting stuff later
12:11 PM
it is an optimization after all so you don't need it from the start. if you just keep namespaces separate it'll be trivial to split out later
12:12 PM
once I can actually see what you are doing I can make a suggestion and maybe a custom target to do what you need

Also look at https://github.com/thheller/shadow-cljs/blob/master/packages/shadow-cljs/cli/runner.js

In your package.json you'll specify a bin. that should be the runner. as seen here https://github.com/thheller/shadow-cljs/blob/master/packages/shadow-cljs/package.json#L21

Try pre-compiled nbb sources in native image + graaljs / node

This would mostly be a fun issue and not yet something which I consider as a serious distribution of nbb: a native image which contains node + the sources of NBB loaded at build time and then the rest of the evaluation would happen at runtime.

Better error handling

It seems in the sqlite3 honeysql example, a lot of times errors aren't reported but the script just outputs nothing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.