shadow-cljs 2025-10-09

@thheller hi Thomas! I want to get your opinion if something would be a worthwhile addition to shadow-cljs We're consuming cljs output from TypeScript (so cljs in this case is way to share logic with Clojure backend), and there is a fair share of functions with defined schema (using malli). I've adjusted our macros to put JSDoc in var's metadata, and it worked out nicely in a sense that it's present in cljs output. But we're using :npm-module as a target and there Object.defineProperty effectively shadows actual functions from JS editors (we've checked vscode and zed), and so they see no types at all. So the question is: would you be open for a PR that copies metadata (or, JSDoc output, to be more precise) on Object.defineProperty calls? (it's not just copying because of this get: stuff, but I think we found an adequate way for TS to see those types). Please tell me if I'm not providing enough context 😁

sorry, was way for a day. ok, so my idea would be to write a build hook. in the flush state in can inspect the build state. https://shadow-cljs.github.io/docs/UsersGuide.html#build-hooks

you can do (get-in build-state [:compiler-env :cljs.analyzer/namespaces 'foo.bar]) to get the full metadata for that ns. for example :defs, each var you can inspect an look for you special metadata. then for each namespace just emit the .d.ts file from there. I don't actually know what they look like, but I think its just bare constructs with no actual code right?

🔥 1

try adding :build-hooks [(shadow.build/tap-hook)] to your build config. it'll just (tap> build-state) and using http://localhost:9630/inspect you can explore it a bit. you should find all the info you need, pretty much only need the :compiler-env but there is also :sources and :output for the generated files

you can use (shadow.build.data/output-file build-state "some.ns.d.ts") to get a java.io.File handle for a file in the :output-dir of the build. or just use any other method to write files. I'm not actually sure where the .d.ts files go 😛

for example this is how the :npm-module target generates its exports for optimized builds. same concept really. I bet you can copy half that code. https://github.com/thheller/shadow-cljs/blob/2602e0b9f8fcf3d041b7b5800d53f025f798daad/src/main/shadow/build/closure.clj#L873-L897

other option is of course eschewing defineProperty so that typescript compiler/editors can see the real functions directly not sure what's the reasons behind the decision to use defineProperty

wouldn't it be easier to just generate .d.ts files with all the infos you want? I still have never used Typescript, so not actually sure how it does anything.

defineProperty is used during development, so that hot-reload can update the references, because of the getter

release builds won't have that

what would it look like to have jsdocs on defineProperty? not quite sure how that works since I assumed that TS doesn't do anything dynamically at runtime, rather static analysis at compile time?

somehow editors figure out when we copy jsdoc over to defineProperty, which is quite valuable during dev time 🙂 I'll be back with an example d.ts makes lots of sense, but I'm unsure how to tackle this :\