shadow-cljs

Pepijn de Vos 2025-08-25T11:38:25.492289Z

I wonder if I can somehow set up shadow-cljs to compile to a couchdb design document? https://docs.couchdb.org/en/stable/query-server/javascript.html#commonjs

thheller 2025-08-25T11:55:03.233779Z

the design document itself probably no. that looks to be a JSON object not JS code. the example does use require though, so files that can be required and used shadow-cljs can create yes. best via :target :npm-module probably

thheller 2025-08-25T11:56:13.680009Z

kinda hard to say. the docs are a bit thin and contain no description of the runtime capabilities

Pepijn de Vos 2025-08-25T11:57:38.295749Z

yeah it's a bit unclear. I do know it's the mozilla js engine and not nodejs, and it seems like you may need to (ns views.lib) and then (defn ^:export map)

thheller 2025-08-25T11:58:09.860989Z

it may require some additional glue. I doubt it can consume the CLJS code directly

thheller 2025-08-25T11:58:26.671129Z

but the doc section you linked has a JS example and a JSON doc

thheller 2025-08-25T11:58:32.763099Z

the JSON doc you likely need to write by hand

thheller 2025-08-25T11:59:12.336579Z

the JS really depends on the runtime capabilities if it works or not

thheller 2025-08-25T11:59:52.427749Z

a release :node-library build will probably work, but the watch/compile probably not since that expects an actual node.js runtime

thheller 2025-08-25T12:00:28.663399Z

> path – A CommonJS module path started from design document root

thheller 2025-08-25T12:00:48.543219Z

dunno what a design document root is, but if you put :npm-module files there they should be loadable

Pepijn de Vos 2025-08-25T12:01:30.745919Z

Maybe I shouldn't bother and just write the JS but I kinda want to.

thheller 2025-08-25T12:02:41.995019Z

probably takes a few minutes to test, so if you have that go for it. happy to answer questions that come up

Pepijn de Vos 2025-08-25T12:09:12.971969Z

what I don't fully understand is how to make sure to export views.lib.map as a function.

Pepijn de Vos 2025-08-25T12:11:16.072769Z

like, in

:modules {:demo {:exports {hello demo.lib/hello}}}
what determines the actual exported function?

Pepijn de Vos 2025-08-25T12:11:25.777709Z

I mean, the namespace of it

thheller 2025-08-25T12:12:59.015389Z

I don't have a clue what a view.lib.map function is

Pepijn de Vos 2025-08-25T12:13:06.632839Z

like, does my cljs namespace need to be named views.lib or is it irrelevant and do I just need :exports map

Pepijn de Vos 2025-08-25T12:13:48.135889Z

I'm just concerned about getting couchdb and clojurescript on the same page on the namespaces

thheller 2025-08-25T12:13:53.424699Z

not a clue. that is the couchdb question 😛

thheller 2025-08-25T12:14:15.428289Z

JS and couchdb likely do not have namespaces at all

Pepijn de Vos 2025-08-25T12:14:44.604319Z

okay so don't worry about it and just export the function under that name

thheller 2025-08-25T12:15:35.719539Z

I don't know. :modules {:demo {:exports {hello demo.lib/hello}}} is neither a valid :npm-module config nor a valid :node-library config, so I'm unsure what you are working with right now

Pepijn de Vos 2025-08-25T12:16:19.536699Z

eh I just copied that from https://shadow-cljs.github.io/docs/UsersGuide.html#_module_exports

Pepijn de Vos 2025-08-25T12:16:31.619269Z

so how DO i export a :node-library function?

thheller 2025-08-25T12:17:18.163819Z

that example is :target :esm

Pepijn de Vos 2025-08-25T12:17:20.704299Z

something like this?

{:target    :node-library
                    :output-to "out/demo-library/lib.js"
                    :exports   {:hello demo.lib/hello}}

thheller 2025-08-25T12:17:25.829719Z

stick to the relevant section 😉

Pepijn de Vos 2025-08-25T12:17:27.688379Z

from https://shadow-cljs.github.io/docs/UsersGuide.html#_full_example

thheller 2025-08-25T12:17:49.860659Z

yep that gives you a hello export

thheller 2025-08-25T12:18:14.313999Z

so with (ns demo.lib) (defn hello [a b] (+ a b)) that is a function that adds the two args

Pepijn de Vos 2025-08-25T12:18:48.715589Z

okay I think I'm starting to understand...

thheller 2025-08-25T12:20:01.054939Z

ah hmm I think I now understand what they mean by "design document root"

thheller 2025-08-25T12:20:07.974439Z

{
    "views": {
        "lib": {
            "security": "function user_context(userctx, secobj) { ... }"
        }
    },
    "validate_doc_update": "function(newdoc, olddoc, userctx, secobj) {
        user = require('views/lib/security').user_context(userctx, secobj);
        return user.is_admin();
    }"
    "_id": "_design/test"
}

thheller 2025-08-25T12:20:19.212149Z

so the validate_doc_update isn't actually accessing any external files

thheller 2025-08-25T12:20:39.406629Z

its accesing the views/lib/security section of that doc itself

Pepijn de Vos 2025-08-25T12:20:49.137329Z

correct

thheller 2025-08-25T12:20:50.389099Z

so yeah, that won't work

Pepijn de Vos 2025-08-25T12:21:50.997899Z

why not?

thheller 2025-08-25T12:22:24.255159Z

because its basically all one big JSON document

thheller 2025-08-25T12:22:52.748709Z

vs. .js files generated and readable from disk

thheller 2025-08-25T12:23:27.598729Z

function user_context(userctx, secobj) {
    var is_admin = function() {
        return userctx.indexOf('_admin') != -1;
    }
    return {'is_admin': is_admin}
}

exports['user'] = user_context

thheller 2025-08-25T12:23:43.041469Z

this part shadow-cljs can generate, but I'm unsure how this would be used. the docs don't explain it

Pepijn de Vos 2025-08-25T12:23:52.577699Z

Yes you'd have to embed the generated js into a json document

thheller 2025-08-25T12:23:56.549949Z

"views": {
        "lib": {
            "security": "function user_context(userctx, secobj) { ... }"
        }
    },

thheller 2025-08-25T12:24:11.096399Z

looks like that is just embedded there, which doesn't make sense

thheller 2025-08-25T12:24:54.496859Z

I mean I suppose you can write an extra script that takes all the generated .js files and turns then into a big JSON docs, but I wouldn't hold my breath for that actually working

thheller 2025-08-25T12:25:16.619359Z

definitely gets more complicated too

thheller 2025-08-25T12:30:14.228619Z

well actually a release :node-library build that is manually inlined that way could probably work

thheller 2025-08-25T12:32:43.265439Z

{
    "views": {
        "lib": {
            "cljs": "<full node-library release output file contents>"
        }
    },
    "validate_doc_update": "function(newdoc, olddoc, userctx, secobj) {
        return require('views/lib/cljs').some_export(userctx, secobj);
    }",
    "_id": "_design/test"
}

thheller 2025-08-25T12:33:32.986249Z

like that with (ns demo.lib) (defn foo [userctx secobj] ....) and :exports {:some_export demo.lib/foo} in the config

Pepijn de Vos 2025-08-25T12:45:00.296749Z

yeah that is what I'm thinking

Pepijn de Vos 2025-08-25T12:45:44.255309Z

I'm only a bit puzzled by > The lib structure can still be used for view functions as well, by simply storing view functions at e.g. views.lib.map, views.lib.reduce, etc.

Pepijn de Vos 2025-08-25T13:09:43.528459Z

Hm maybe I could generate the json in a build hook

Pepijn de Vos 2025-08-25T13:10:09.701739Z

can I install clojure build depds somehow? data.json for example. Or just use string interpolation

thheller 2025-08-25T13:58:32.563629Z

you can write a clojure function to do that yes. just a regular clojure function that shadow-cljs doesn't need to know anything about.

Pepijn de Vos 2025-08-25T14:09:05.831379Z

I was thinking a build hook would be nice since you can just build the js and it'd automatically produce the whole design document but I guess you're limited to core clojure in that case

thheller 2025-08-25T14:09:44.144989Z

not really sure what you are thinking

thheller 2025-08-25T14:11:07.560779Z

in a setup like that. you create a (repl/build-the-thing) function. that function can call (shadow/release :your-build) and after that its just a regular clojure function which can do everything clojure can usually do. including loading and using other dependencies. its just normal clojure, no limits.

thheller 2025-08-25T14:11:44.044359Z

yes, you could also do it as a build hook, but I'd keep it separate. makes its easier to experiment and tweak.

thheller 2025-08-25T14:11:59.469179Z

heck could even have the function directly upload the thing to the couchdb server

Pepijn de Vos 2025-08-25T14:12:16.378229Z

hmmmm

Pepijn de Vos 2025-08-25T14:12:19.582679Z

currently I have https://github.com/NyanCAD/Mosaic/blob/main/src/main/nyancad/mosaic/build_hooks.clj

thheller 2025-08-25T14:14:11.780639Z

sure that works. but returning the build-state unchanged usually means that this probably shouldn't be a build hook at all 😛

thheller 2025-08-25T14:17:35.342739Z

see the example release fn. if you run that instead you basically get the same result, but can structure the code so you can run the function whenever and independent of what shadow-cljs is doing.

Pepijn de Vos 2025-08-25T15:06:44.549979Z

okay that's actually a lot nicer now. Any I think I have a cljs view function working!

👍 1
Ryan 2025-08-25T20:09:12.317269Z

If I have to add a github repo dependency, is the right move to use deps.edn for that, and if so do I need to use all deps.edn or can I mix deps.edn and the in-line depencencies from shadow-cljs.edn?

borkdude 2025-08-25T21:57:25.862279Z

you can put them in deps.edn in an alias and in shadow-cljs you can say :deps {:aliases [:foo]} if I recall correctly

borkdude 2025-08-25T21:57:46.441479Z

I don't know if they mix, I wouldn't be surprised if they didn't

borkdude 2025-08-25T21:59:23.377309Z

it's documented here: https://shadow-cljs.github.io/docs/UsersGuide.html#deps-edn

thheller 2025-08-26T06:57:33.372949Z

they do not mix. so everything goes into deps.edn with or without aliases