shadow-cljs 2025-08-25

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

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

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

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)

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

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

the JSON doc you likely need to write by hand

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

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

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

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

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

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

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

like, in

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

I mean, the namespace of it

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

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

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

not a clue. that is the couchdb question 😛

JS and couchdb likely do not have namespaces at all

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

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

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

that example is :target :esm

something like this?

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

stick to the relevant section 😉

yep that gives you a hello export

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

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

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

{
    "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"
}

so the validate_doc_update isn't actually accessing any external files

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

correct

so yeah, that won't work

why not?

because its basically all one big JSON document

vs. .js files generated and readable from disk

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

exports['user'] = user_context

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

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

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

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

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

definitely gets more complicated too

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

{
    "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"
}

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

yeah that is what I'm thinking

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.

Hm maybe I could generate the json in a build hook

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

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.

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

not really sure what you are thinking

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.

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

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

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

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.

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

👍 1

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?

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

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

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