Is there a way to make cljs-devtools work with scittle? Working with the stuff like on the pic is no fun.
@p-himik Looking at improving the stacktrace. When I do this:
cljs.user=> (->> (try (sci/eval-string "(defn broken [] [:oh :boy :this :function (js/is_sure) :a :large :one])") (catch js/Error e e)) (sci/stacktrace) (sci/format-stacktrace) (run! prn))
"user/fn - <expr>:1:43"
"clojure.core/fn - <built-in>"
"user - <expr>:1:1"
"clojure.core/defn - <built-in>"
"user - <expr>:1:1"
"user - <expr>:1:1"I see a quite ok stacktrace?
oh I see
I need to add 'js
yeah bingo:
cljs.user=> (->> (try (sci/eval-string "(defn broken [] [:oh :boy :this :function (js/is_sure) :a :large :one])" {:classes {'js js/globalThis :allow :all}}) (catch js/Error e e)) (sci/stacktrace) (sci/format-stacktrace) (run! prn))
nilcljs.user=> (->> (try (sci/eval-string "(defn broken [] [:dude (js/is_sure) :foo]) (broken)" {:classes {'js js/globalThis :allow :all}}) (catch js/Error e (prn (ex-message e)) e)) (sci/stacktrace) (sci/format-stacktrace) (run! prn))
"Function.prototype.apply was called on undefined, which is a undefined and not a function"
"user/broken - <expr>:1:24"
"user/broken - <expr>:1:1"
"user - <expr>:1:44"
πThe error message still isn't great when happening in a callback. I'm thinking about how this can be improved... it's hard...
Anyway perhaps I can cut a scittle release with dev files. Perhaps it would be best if I could put the production + dev files into one release, so you don't accidentally mix those up. I can also name all the dev files scittle.dev.js for example
and then you get scittle.js + scittle.js.map + + all in one release
That would also fix this issue: https://github.com/babashka/scittle/issues/114
does cljs-devtools work with advanced compiled JS? that might be the problem? or should I make a cljs-devtools plugin for scittle? that might work
so it can be compiled along
> does cljs-devtools work with advanced compiled JS? Doesn't seem like it, from https://github.com/binaryage/cljs-devtools/blob/master/docs/faq.md#why-custom-formatters-do-not-work-for-advanced-builds: > There is a technical glitch which currently prevents CLJS devtools to work under :advanced optimizations. Some http://dev.clojure.org/jira/browse/CLJS-1249 and that is why CLJS DevTools cannot recognize objects belonging to CLJS land.
that may be challenging then unless you compile scittle yourself
can't believe the alternative set! syntax wasn't supported yet in SCI
the next scittle will, just pushed it
Is it in a plugin or shipped in the main js file?
Ambiguous what youβre referring to
I mean is cljs-devtools now compiled into the main scittle.js distributed file that will be on e.g. CDNs?
Or is it in a separate plugin file?
this https://clojurians.slack.com/archives/C034FQN490E/p1755023419169069?thread_ts=1755019363.681209&cid=C034FQN490E was referring to the alternative set syntax
see the message just before it
I can see how it confuses you, but it was about an issue that @p-himik posted as well
if you have any ideas how to make cljs-devtools work, feel free to share them
Perhaps I can distribute both a development and production version of scittle
Oh sorry, I completely misunderstood!
No worries. Would be nice if we could make it work though
Yes that would be cool. I miss it from shadow-cljs dev.
I don't know what this means: https://github.com/binaryage/cljs-devtools/issues/37 Does it mean that cljs-devtools is always disabled using advanced compilation?
I think it is always meant to be disabled, even if it's not used. But that FAQ entry I mentioned hints that even if properly included, it might still not work because advanced optimizations remove some important data.
yeah. I could compile scittle using :simple for development but I don't know if shadow-cljs supports it... could try.
Why not just :whitespace for development?
what's the difference between whitespace and simple?
also performs optimizations within expressions and functions, including renaming local variables and function parameters to shorter namesIn other words, :simple makes debugging much worse.
alright, with :simple I get output like this:
$APP.cljs.core.map_indexed.cljs$core$IFn$_invoke$arity$2=function(a,b){return function f(d,e){return new $APP.cljs.core.LazySeq(null,function(){var g=$APP.cljs.core.seq(e);if(g){if($APP.cljs.core.chunked_seq_QMARK_(g)){for(var h=$APP.cljs.core.chunk_first(g),k=$APP.cljs.core.count(h),l=$APP.cljs.core.chunk_buffer(k),m=0;;)if(m<k)$APP.cljs.core.chunk_append(l,function(){var p=d+m,q=$APP.cljs.core._nth(h,m);return a.cljs$core$IFn$_invoke$arity$2?a.cljs$core$IFn$_invoke$arity$2(p,q):a.call(null,p,q)}()),
That seems to preserve the necessary info. I'll try whitespace as well.Renaming aside, I'm pretty sure it also straight up reuses some vars.
with whitespace:
goog.functions.rateLimit=function(f,interval,opt_scope){let timeout=0;const handleTimeout=function(){timeout=0};return function(var_args){if(!timeout){timeout=goog.global.setTimeout(handleTimeout,interval);f.apply(opt_scope,arguments)}}};goog.functions.isFunction=val=>{return typeof val==="function"};goog.provide("goog.string.TypedString");goog.string.TypedString=function(){};goog.string.TypedString.prototype.implementsGoogStringTypedString;goog.string.TypedString.prototype.getTypedStringValue;goog.provide("goog.string.Const");goog.require("goog.asserts");goog.require("goog.string.TypedString");goog.string.Const=function(opt_token,opt_content){this.stringConstValueWithSecurityContract__googStringSecurityPrivate_=opt_token===goog.string.Const.GOOG_STRING_CONSTRUCTOR_TOKEN_PRIVATE_&&opt_content||"";this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_=goog.string.Const.TYPE_MARKER_};goog.string.Const.prototype.implementsGoogStringTypedString=true;(just a random sample)
if I now would include cljs-devtools as a plugin I think it would work
3144729 13 aug. 11:06 scittle.js
only 3mb :)
but it does work, so that's promising
And gzipped?
dunno
Should be much, much smaller due to all the unoptimized repetitions. Probably less than 1 MB.
hm I get all kinds of errors like:
Uncaught TypeError: Cannot set properties of undefined (setting 'NodeType')
at scittle.js:95:1083
at scittle.js:7006:4Understand this error
deps.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)goog.dom.NodeType =
seems to not workI pushed this to a branch named scittle-dev . Feel free to take a peek. you can compile it with bb prod and the JS files are in resources/public/js. if you start an http server that serves resources/public you can make an html file there and play around.
You use shadow-cljs, right?
Might be that :simple does more there than the GCC docs describe. If so, maybe :simple should still be used, but with :pretty-print and :pseudo-names compiler options set to true.
shadow yes
this is using whitespace though
I'll try simple again
That's what I'm saying - maybe :whitespace doesn't do something in shadow-cljs that's required for Scittle. Given the error, it's probably related to goog modules.
And if so, then :whitespace is simply a no-go without also altering shadow-cljs. But of course Thomas would know for sure.
lol, with simple:
scittle.js:242 Uncaught SyntaxError: Invalid regular expression: /[Γ'-ΓΒ―ΓΒΊ-à £¿Òβ¬οΏ½-οΏ½οΏ½-οΏ½Γ―Β¬-Γ―Β·ΒΏΓ―ΒΉΒ°-ﻼ]/: Range out of order in character class (at scittle.js:242:197)
at new RegExp (<anonymous>)
at scittle.js:242:197
at scittle.js:5866:4I have to step away from this for a bit, but feel free to poke around yourself if you're interested
Ugh, I hate that error. It happens something like once a year - infrequent just enough to never remember what the fix is. I think once it was fixed by updating shadow-cljs. And another time by updating some NPM package.
I'm a bit behind on shadow so that may be the issue
Bumping shadow worked
and :whitespace didn't work at all, so simple it is
those extra options, should they look like this?
:compiler-options {:optimizations :simple
:pretty-print true
:pseudo-names true}I believe so, yes.
I made a devtools plugin for scittle, but I'm seeing this in the console when I enable it.
scittle.cljs-devtools.js:921 NOT installing CLJS DevTools 1.0.7 under advanced build. See .
pfffffand that faq link gets me a 404. faq this
pushed the changes to scittle-dev
Example html:
<html>
<head>
<script src="js/scittle.js" type="application/javascript"></script>
<script src="js/scittle.cljs-devtools.js" type="application/javascript"></script>
<script type="application/x-scittle">
(js/console.log {:a 1})
</script>
</head>
<body>
</body>
</html>ah:
> Since version 0.8.3, cljs-devtools detects advanced build situation and refuse to install. If you want to disable this check https://github.com/binaryage/cljs-devtools/blob/master/docs/configuration.md :disable-advanced-mode-check to true prior installing.
ok works now:
Awesome!
pushed it to the branch
I'm still not sure if it would solve your original problem though. the uncaught exception still looks cryptic (because it contains internal impl details of SCI)
so best to just try this and see how this improve the DX
I'll check.
so: clone scittle, checkout branch scittle-dev, run bb prod , use js files from resources/public/js
and include <script src="js/scittle.cljs-devtools.js" type="application/javascript"></script>
perhaps I could just merge this cljs-devtools thing with the main scittle file since it's for debugging anyway
but that's something we can decide later
BTW is Scittle "interpret always" or more closer to JIT compilation? I'd assume it's the latter.
scittle is based on SCI which is an interpreter. what makes you assume it does JIT?
I'm probably misnaming things.
So when it sees (defn f [] ...) and later at some point (do (f) (f)), the whole f has already been interpreted and is in the memory, right? So everything is interpreted at most once.
yes, for sure. it works pretty much like JVM Clojure. function f is analyzed + evaluated and then is in memory
the body of f is still interpreted though. it's not as fast as the compiled version.
but there is a separate analysis + eval phase, so most things are done in the analysis phase (when possible)
Right, that was my intuition, thanks.
I think I could also make a scittle version of https://squint-cljs.github.io/squint/ btw, which would then be fully compiled
but then you wouldn't get reagent, etc
that would work if I used cherry instead
Tried scittle-dev with cljs-devtools. The error is incomparably better! Butt-ugly because of the dark theme in Firefox, but at least it's easy to see the actual info.
Unfortunately, as expected, it didn't affect the stack trace itself.
As for the reproduction of an incomplete stack trace - the symptom is a bit different but the essential problem is still there with this:
(defn broken []
[:oh :boy
:this :function
(js/is_sure)
:a :large :one])
(broken)
It results in the error on the pic.
As you can see, nothing mentions the line with (js/is_sure). The line 125 is where broken is defined.Aha, for that explicit nice Scittle error report to disappear, all you have to do is call (broken) in a JS event handler:
(defn broken []
[:oh :boy
:this :function
(js/is_sure)
:a :large :one])
(let [b (js/document.createElement "button")]
(set! (.-textContent b) "Trigger error")
(.addEventListener b "click"
(fn [_]
(broken)))
(js/document.body.append b))yes, perhaps the scittle error report can be done on uncaught exceptions as well, currently only synchronous errors are caught and logged this way
I guess js interop doesn't retain the location in the stacktrace, this can be fixed, let me log a SCI issue
logged it here: https://github.com/babashka/sci/issues/986
Awesome.
Yeah, I can see now that (im-actually-nil) properly shows that location, nice.
> merge this cljs-devtools thing with the main scittle file would this increase the size a lot?
$ l public/scittle.*
-rw-rw-r-- 1 p-himik p-himik 441K Aug 13 20:49 public/scittle.cljs-devtools.js
-rw-rw-r-- 1 p-himik p-himik 4.6M Aug 13 20:47 public/scittle.js> dark theme in Firefox You can get a dark theme in shadow-cljs like this, not sure how to make that work in scittle:
:compiler-options
{:external-config
;; these are for legibility when using dark theme devtools
;; any of these can be overwritten:
;; -> defaults.cljs
{:devtools/config
{:keyword-style "color: #909113;"
:cljs-land-style "background: rgba(0,0,0,0);color:#eee;
border-radius:2px;"
:string-style "color: #16b4c4;"
:symbol-style "color: rgb(239,194,194);"
:body-style "display:inline-block;padding:3px 12px;
border-top:2px solid rgba(60,90,60,.1);
margin:1px;margin-top:0px;
background:transparent;"
}}} Not sure if it can be set at runtime
for me personally i'd prefer dev-tools as a plugin
I mean just for the dev version of scittle. You wouldnβt use that for prod anyways
Am I doing something wrong? A very basic thing seems to be broken. Just as a sanity check, I have code like this:
(js/console.log #js {:cv js/cv
:mat (.-Mat js/cv)
:mat2 (unchecked-get js/cv "Mat")})
And here's the result. Mat is not there when in Scittle but is there when in JS.But it does work if I replace js/cv with my own #js {:Mat 1}. Huh.
Aaaand refreshing without cache has fixed it. sigh Bloody web development, always some random bullshit.
Nah, false alarm - it worked exactly once, for an unknown reason. And I can't make it work again.
@borkdude There's no js* in Sci or some alternative, is there?
@p-himik can you give more context of what you're trying to do? I may be able to explain some corner case
there is no js* but you can use js/eval
or at least js/globalThis.eval if that doesn't work
I'm just trying to use OpenCV.js.
In JS, I had new cv.Mat() which was working all the time.
Decided to rewrite in in Scittle, so it became (js/cv.Mat.) which was throwing. Tried debugging - apparently (.-Mat js/cv) is undefined. Well, almost always anyway.
Ah, right, eval. Will check.
how is cv created
gimme the HTML and I'll try it
This works for me:
<html>
<head>
<script src="" type="application/javascript"></script>
<script src=""></script>
<script type="application/x-scittle">
(js/console.log js/cv)
(js/console.log js/cv.Mat)
(js/console.log (js/cv.Mat.))
</script>
</head>
<body>
<button onclick="my_alert()">
Click me!
</button>
</body>
</html> are you by any chance loading opencv via an ESM thing using an async script tag?
I tried using defer initially, but it's supposed to execute scripts in order anyway.
But the behavior without defer is still the same.
I have this in my <head>:
<script src=""></script>
<script src=""></script>
<script src="/public/main.cljs" type="application/x-scittle"></script>
<script type="application/x-scittle">
(js/console.log "from tag, immediate" (.-Mat js/cv))
(js/setTimeout #(js/console.log "from tag, delayed" (.-Mat js/cv)) 1000)
</script>
And this in /public/main.cljs:
(js/console.log "from file, immediate" (.-Mat js/cv))
(js/setTimeout #(js/console.log "from file, delayed" (.-Mat js/cv)) 1000)
The output:
from file, immediate undefined
from tag, immediate undefined
from file, delayed function Mat()
from tag, delayed function Mat()Bah, this is some OpenCV.js crap, I bet. I can now see the same behavior in JS. It was masked before by some other code that became irrelevant in the Scittle version.
ah if the same happens in JS, I'm relieved ;)
I see the same indeed
Module = {
onRuntimeInitialized() {
// this is our application:
console.log(cv.getBuildInformation())
}
}
// Load 'opencv.js' assigning the value to the global variable 'cv'
cv = require('./opencv.js')This is from the node docs, perhaps browser is same?
@p-himik ok, discovered this:
<html>
<head>
<script src=""></script>
<script src=""></script>
<script type="application/x-scittle">
(set! (.-onRuntimeInitialized js/cv) (fn [] (prn :dude)))
</script>
</head>
<body>
<button onclick="my_alert()">
Click me!
</button>
</body>
</html> it has a callback that it calls when it's done loading
Oh, thank you!
I see now that the docs do mention onRuntimeInitialized, but only at the very bottom of the tutorial, not at the start, where it makes more sense.
What would be a proper way to debug things?
E.g. I'm getting an error like on the pic below, which is inscrutable.
The e.data.toString() gives me a more useful
'{:type :sci/error, :line 107, :column 26, :message "d is not a function", :sci.impl/callstack #object[cljs.core.Volatile {:val ({:line 107, :column 26, :ns #object[qs main], :file "/public/main.cljs", :sci.impl/f-meta {:name process-frame, :ns #object[qs main], :file "/public/main.cljs", :arglists ([ctx video canvas]), :line 39, :column 1}})}], :file "/public/main.cljs"}'
But it still doesn't include the full stack trace - it's just two levels deep. Line 107, where process-frame is called, and line 39, where it is defined. But the error is clearly somewhere inside process-frame, and that line number is missing.Perhaps this helps? https://github.com/babashka/scittle/discussions/126
if the line number is missing, we can possibly improve that in SCI, repro welcome
I usually insert lots of prn / js/console.log to debug stuff in SCI (bb, scittle, etc) scripts if the stacktrace isn't sufficient
I'll try that, thanks.
In the mean time, onRuntimeInitialized did not work.
I had to follow the other suggestion in the tutorial - to check if js/cv is a promise...
was it a promise?
It's race-y.
weird, the callback thing did work for me
Might depend on the internet, or on the rest of the code. π€·
What I meant is that I did get :dude printed every single time I refreshed the page with this sample:
https://clojurians.slack.com/archives/C034FQN490E/p1755028182333969?thread_ts=1755019729.288109&cid=C034FQN490E
but of course I haven't test anything else
afk for some sleep now
When there's enough code, it seems that the module might get initialized before the set! is called.
facepalm
maybe it has a function for that to check?
or property
like the constructor you were going to call
Eh. Not too keen on digging deeper, it's all just to fool around. :) The promise check works for now.
k
one more thought I had: perhaps it works when you create the cv object yourself and attach that callback property. so before the script that loads cv. not sure if can handle that. but anyway.