Fork me on GitHub
#shadow-cljs
<
2022-10-17
>
Lone Ranger14:10:34

@thheller question if this blasphemy is possible -- I want to use some typescript source code in my clojurescript project. I see that it is possible to https://shadow-cljs.github.io/docs/UsersGuide.html#_access_cljs_from_js ... 1. is it possible to use clojurescript from javascript and include that javscript into the project in a single compilation step, or are multiple compilation steps required? a. Will multiple copies of cljs.core be created, or just 1? 2. If it can be done in a single step, can I do the following: a. cljs.core -> javascript -> project cljs files -> other local javascript -> other local source files?

partyparrot 1
Lone Ranger14:10:30

My usecase for this heresy: I'm trying to practice some so-called "Domain Oriented Design" where the business logic closely matches the common sense notions of the business rules. I'm trying to make it open for extension but closes for modification, so I want the code to be polymorphic and compile-time extensible. I can do this with defprotocol/defrecord but it's too slow and there's too much code duplication, so I'm dipping into typescript. However -- I want to use all the cljs functions. Then I want to use those datatypes in my clojurescript library code. Then I want to use the clojurescript library code in the javascript functions to make more sophisticated datatypes. Then I want to use THOSE more advanced datatypes in the clojurescript app code.

Lone Ranger14:10:01

More detailed notes:

// baseData.ts

    get: () => any
}

interface ISettable<out T> {
    set(x: any): T;
}

export interface IGetSet<out T> extends ISettable<T>, IGettable {

}

type MapType = cljs.core.PeristentHashMap | cljs.core.PersistentArrayMap;

export class MyData implements IGetSet<MyData> {
    cljsData: MapType


    constructor(data: MapType) {
        this.cljsData = data;
    }

    get(): any {
        cljs.core.get(this.cljsData, cljs.keyword("data"))
    }

    set(x: any): MyData {
        let existingData = this.cljsData;
        let path = cljs.keyword("data");
        let newData = cljs.core.assoc(existingData, path, x);
        return new MyData(newData)
    }
}
(ns somens.core
  (:require ["./path/to/src/gen/baseData.js" :as my-data]))


(defn ^:export fancy-cljs-fn [my-data]
  (.set my-data 42))
// fancyData.ts
import {fancy_cljs_fn} from "???"; // help needed
import {IGetSet, MyData} from "./baseData";

class FancyData implements IGetSet<FancyData> {
    mydata: MyData;

    constructor(data: MyData) {
        this.mydata = fancy_cljs_fn(data);
    }

    get(): any {
        return this.mydata.get();
    }

    set(x: any): FancyData {
        let newData = this.mydata.set(x);
        return new FancyData(newData);
    }

}
(ns fancyns.core
  (:require ["./path/to/src/gen/fancyData.js" :as fancy]))

(defn ^:export super-fancy [fancy-data]
  "yay")

Alexis Schad14:10:39

wtf xD for requiring cljs in JS, there's the doc : https://shadow-cljs.github.io/docs/UsersGuide.html#_access_cljs_from_js import {fancy_cljs_fn} from "goog:somens.core" Did you already try it?

1
Lone Ranger14:10:35

Ohhh ok I thought that "goog:cljs.core" was a hardcoded constant or something, didn't realize that was extensible

Lone Ranger14:10:02

Ok there's no way this is possible in the same compiler pass.

Lone Ranger14:10:26

Or is it :thinking_face:

Alexis Schad14:10:43

Don't know but I want to know 😅

1
Alexis Schad14:10:45

The JS file is not processed by shadow, it is used directly as it is, so I think it should work. With a parallel TS watcher of course.

Lone Ranger14:10:13

this will be wild

Lone Ranger15:10:07

I think it's gonna work but ran out of time

Lone Ranger15:10:11

will try again soon

👍 1
Lone Ranger16:10:01

damn it actually works

Lone Ranger16:10:14

I can't even believe this works 😮

Lone Ranger16:10:40

I think this must be what Voldemort felt like when he got into the blood magic section of the library

2
Alexis Schad20:10:29

nice, hope your new weird stack will fit your needs

😅 2
Lone Ranger20:10:19

It will I think as soon as I get hot reloading to work!

Lone Ranger16:10:26

Is there a way to hot reload userland javascript?

Lone Ranger16:10:40

but I'm not observing this behavior

rolt16:10:50

are you sure your js files are in your source paths ? hot reload works for me

Lone Ranger16:10:18

ok, probably on my end then

Lone Ranger16:10:50

does it watch subdirectories, too? so for instance if I have src/gen in :source-paths, will it watch src/gen/sub, or do I need to mention that explicitly?

Lone Ranger17:10:01

Yeah if anyone has any more suggestions I'd be appreciative. The change is not being recompiled to js/cljs-runtime.

{:source-paths
 ["src/cljs"
  "src/gen"]
 :deps true
 :dev-http {8080 "public"}
 :nrepl    {:port       9000
            :middleware [cider.piggieback/wrap-cljs-repl]}
 :builds
 {:frontend
  {:target  :browser
   :modules {:main {:init-fn cljstable.core/init}}
   :devtools {:repl-init-ns cljstable.sidecar
              :repl-pprint  true}}}}
Here's the compilation results and relevant directory structure. Am I doing something wrong here? Invoked via cider with:
;;  Startup: /home/jay/.nvm/versions/node/v16.14.0/bin/npx shadow-cljs -d nrepl/nrepl:0.8.3 -d cider/piggieback:0.5.2 -d refactor-nrepl/refactor-nrepl:2.5.1 -d cider/cider-nrepl:0.26.0 server
;;
;; ClojureScript REPL type: shadow
;; ClojureScript REPL init form: (do (require '[shadow.cljs.devtools.api :as shadow]) (shadow/watch :frontend) (shadow/nrepl-select :frontend))

Lone Ranger17:10:55

Even when I delete the js/cljs-runtime/module$cljstable$baseData.js, it gets rebuild with the unchanged code

rolt22:10:29

this makes no sense, unless you have a second js source file with the old code. Or could it have cached the old result somehow ? I don't see why

Lone Ranger22:10:17

yeah, ripping my hair out about it

Lone Ranger22:10:23

will try to start with a fresh project 😞

Lone Ranger22:10:54

uuugh yeah with a fresh project it works. there's some garbage artifacts somewhere. good call @U02F0C62TC1

vemv23:10:50

tangential, but your cider-nrepl (and most notably refactor-nrepl) are outdated :)

Lone Ranger23:10:51

Yeah not sure how to fix that when doing cider-connect-cljs

Lone Ranger23:10:54

Ok I FINALLY figured out what it was... sort of

Lone Ranger23:10:56

so it seems that I need to reference at least one of the js file in the :init-fn namespace in order for shadow-cljs to recognize a recompile

👍 1
vemv23:10:54

> Yeah not sure how to fix that when doing cider-connect-cljs You'd have to have a look at whatever is your nrepl server (maybe some webapp?)

Lone Ranger23:10:52

yarn shadow-cljs watch frontend then whatever cider does when connecting

Lone Ranger23:10:11

yeah, confirmed, there needs to be some kind of reference to the javascript file in the :init-fn namespace in order to recompile... phew... thank god... can move on with my life now...

vemv23:10:49

taking a quick look at the docs, looks like shadow-cljs picks up whatever cider-nrepl version is defined in either your project.clj or deps.edn. The docs also reference a ~/.shadow-cljs/config.edn file, although it looks like it's not much advised

thheller08:10:47

@U3BALC2HH as a general rule: shadow-cljs only compiles files that are actually required in a build. so :init-fn is following all :require in the namespaces (or import in JS files) and compiles those. a file that is not required is not compiled, as you learned 😉

thheller08:10:31

also when using :deps true the :source-paths config in shadow-cljs.edn has no effect. you should be warned about that on startup, but depending on how you start things that may not be visible (eg. emacs hiding it in some buffer)

thheller08:10:51

:middleware [cider.piggieback/wrap-cljs-repl] this you don't need at all

thheller08:10:26

as a general warning the JS support is not widely used and can cause issues with externs in release builds. since there is no externs inference those might be hard to debug later

Lone Ranger13:10:05

Makes a ton of sense when you put it like that. There were so many moving parts with that typescript debauchery that I got lost in the sauce

😂 1
Lone Ranger19:10:29

Ok I'm assuming the answer is "because of idiots like me" but I'm really confused why this is not part of standard clojurescript: https://shadow-cljs.github.io/docs/UsersGuide.html#_requiring_js

thheller20:10:47

dunno, nobody ever attempted to get it there?