This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-12-28
Channels
- # announcements (1)
- # aws (1)
- # babashka (15)
- # beginners (19)
- # biff (4)
- # clojars (24)
- # clojure (22)
- # clojure-europe (17)
- # clojure-norway (3)
- # clojurescript (10)
- # emacs (5)
- # gratitude (1)
- # introduce-yourself (2)
- # jobs (1)
- # malli (3)
- # off-topic (2)
- # re-frame (1)
- # remote-jobs (3)
- # shadow-cljs (71)
- # vim (4)
How would I make the :asset-path
"absolute"? My frontend app works fine with routes like /login
, but assets immediately fail to load when I navigate to a route like /app/panel
, since the rendered views will look for assets in /app/assets/...
instead of /assets/...
:asset-path
is only used in a few places for JS files, you just specify an absolute path, so :asset-path "/js"
or so
any other "assets" you control yourself via HTML or code, so just emit absolute paths there
I guess my question is then "is there a natural way to communicate server URL from shadow-cljs.edn to my frontend code?"
Sorry. I just fixed the issue by doing exactly what you suggested. Thanks for the help!
If I have to delete .shadow-cljs
to get a compilation working, that's a bug, right?
markus@ubuntu2204:~/src/specter-demo$ npx shadow-cljs compile :bootstrap
shadow-cljs - config: /home/markus/src/specter-demo/shadow-cljs.edn
shadow-cljs - connected to server
[:bootstrap] Compiling ...
------ ERROR -------------------------------------------------------------------
File: /home/markus/src/specter-demo/src/main/fenster/mcvchcljc.cljc
Exception: No namespace: fenster.mcvchcljc found
clojure.core/the-ns (core.clj:4163)
clojure.core/ns-publics (core.clj:4190)
clojure.core/ns-publics (core.clj:4190)
shadow.build.macros/find-macros-in-ns (macros.clj:67)
shadow.build.macros/find-macros-in-ns (macros.clj:65)
shadow.build.macros/load-macros (macros.clj:121)
shadow.build.macros/load-macros (macros.clj:85)
shadow.build.compiler/post-analyze-ns (compiler.clj:49)
shadow.build.compiler/post-analyze-ns (compiler.clj:46)
shadow.build.compiler/post-analyze (compiler.clj:92)
shadow.build.compiler/post-analyze (compiler.clj:89)
shadow.build.compiler/analyze/fn--13241 (compiler.clj:265)
shadow.build.compiler/analyze (compiler.clj:252)
shadow.build.compiler/analyze (compiler.clj:211)
shadow.build.compiler/analyze (compiler.clj:213)
shadow.build.compiler/analyze (compiler.clj:211)
shadow.build.compiler/default-analyze-cljs (compiler.clj:415)
shadow.build.compiler/default-analyze-cljs (compiler.clj:404)
clojure.core/partial/fn--5908 (core.clj:2642)
shadow.build.compiler/do-analyze-cljs-string (compiler.clj:321)
shadow.build.compiler/do-analyze-cljs-string (compiler.clj:278)
shadow.build.compiler/analyze-cljs-string/fn--13326 (compiler.clj:518)
shadow.build.compiler/analyze-cljs-string (compiler.clj:517)
shadow.build.compiler/analyze-cljs-string (compiler.clj:515)
shadow.build.compiler/do-compile-cljs-resource/fn--13354 (compiler.clj:637)
shadow.build.compiler/do-compile-cljs-resource (compiler.clj:618)
shadow.build.compiler/do-compile-cljs-resource (compiler.clj:572)
shadow.build.compiler/maybe-compile-cljs/fn--13458 (compiler.clj:969)
shadow.build.compiler/maybe-compile-cljs (compiler.clj:968)
shadow.build.compiler/maybe-compile-cljs (compiler.clj:944)
shadow.build.compiler/par-compile-one (compiler.clj:1089)
shadow.build.compiler/par-compile-one (compiler.clj:1044)
shadow.build.compiler/par-compile-cljs-sources/fn--13498/iter--13520--13524/fn--13525/fn--13526/fn--13527 (compiler.clj:1162)
clojure.core/apply (core.clj:667)
clojure.core/with-bindings* (core.clj:1990)
clojure.core/with-bindings* (core.clj:1990)
clojure.core/apply (core.clj:671)
clojure.core/bound-fn*/fn--5818 (core.clj:2020)
java.util.concurrent.FutureTask.run (FutureTask.java:264)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1128)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:628)
java.lang.Thread.run (Thread.java:829)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------markus@ubuntu2204:~/src/specter-demo$
markus@ubuntu2204:~/src/specter-demo$ rm -rf .shadow-cljs/
markus@ubuntu2204:~/src/specter-demo$ npx shadow-cljs compile :bootstrap
shadow-cljs - config: /home/markus/src/specter-demo/shadow-cljs.edn
shadow-cljs - updating dependencies
shadow-cljs - dependencies updated
[:bootstrap] Compiling ...
[:bootstrap] Build completed. (90 files, 90 compiled, 0 warnings, 18.35s)
@U05224H0W related, but not necessarily the same issue: I suspect that at least cljc files on the source path that are intended to and also practically only used for self-hosted CLJS (i.e. :target :bootstrap
) are run through the Clojure compiler during reload when a watch is active for the normal ClojureScript build (`:target :browser`), and then complain about, for example, no such namespace: js
. These files work as intended in the self-hosted environment.
Is this intended to work differently? Can I have self-hosted CLJS-only files in my sources that can assume to be run only in a CLJS context?
the browser build will only compile files it required itself. note that it is extremely easy to make mistakes with .cljc files, thus I rarely recommend using them
bootstrap will also compile more files than other builds do, so it might be deep in some macro files that otherwise are never compiled
I'm still at a stage where I want to make specter work in an self-hosted environment. For that I'm trying to understand how macros work in the self-host environment provided by shadow-cljs :bootstrap. For that I'd like to step through the code and look at stuff. Not sure how to do that though. I'm on emacs and use cider.
there is nothing special to macro compilation. the ns is just compiled as normal, using :cljs
branches in .cljc
files
I believe you, but that doesn't yet match the mental model I have built based on my observations so far 🙂
Something must be different though because js/some.name.space.js
and js/some.name.space$macros.js
are different
I don't know what you mean by that. you should under no circumstance ever refer to any CLJS namespace via js/
if you mean the output files then yes, there are 2. can't write two namespaces into one file
the way you should approach this is that it is basically two entirely separate namespaces, that just happen to have the same name from the code side (so same ns
) but different internally
I mean the output files, yes. The two are different, but they are produced from the same file. So something must be different about the compilation process.
Another way to state the problem is that (macrovich/deftime (defmacro ...))
macros don't make it to the $macros output file for the namespace in my situation currently.
Also I read that defmacro
is ignored – or produces no output – when compiled in CLJS, maybe that's one difference?
More close to my original problem, which is that select
is undefined in CLJS in a self-hosted environment, I seems clear now why the upstream version behaves like this - it has (defmacro select ...)
in #?(:clj
. A PR/fork/branch that was reportedly working better has it in (macrovich/deftime ...)
instead. However, select
still didn't appear in the ...$macro.js
output file.
I assumed that that must be correct, but maybe it isn't. Reading https://cljs.github.io/api/cljs.core/defmacro now, that looks helpful.
Hmm, when the cljs js compiler asks for the source of a namespace as a non-macro namespace, shadow-cljs effectively (modulo analyze caching and so on) offers output generated form .cljs or .cljc files, and when asked for a macro namespace, it offers .cljc or .clj, is that accurate? Does that effectively mean that for bootstrapped / self-hosted cljs we can put CLJS code in .clj files?
Also, if I have code that should run at compile-time, and that code is different for cljs jvm (clj compiles macro ns) and cljs js (cljs compiles macro ns) and I want separate files for macro ns from non-macro ns, I end up with a .clj file with #?(:cljs
reader conditionals, correct?
The original specter source code for the select macro: https://github.com/redplanetlabs/specter/blob/master/src/clj/com/rpl/specter.cljc#L347 And the PR: https://github.com/jeff303/specter/blob/bootstrap/src/clj/com/rpl/specter.cljc#L359
I have a local repo that shows that defmacros in macrovich/deftime in cljc files don't end up in the compiled js and are thus not available for source code to be compiled by the self-hosted compiler.
deftime seems useless to me here, as is the conditional. just remove it entirely, so the code is always loaded
I mean I can't tell if there is any actual java interop in there, but otherwise if its just clojure code just keep it always active
Thanks, I'll try that. I still need to understand it though 🙂 So defmacro
s` in macrovich/deftime
aren't supposed to end up in the JS output?
the problem is that #(:clj ..)
hides it from the CLJS macro compilation which will take the :cljs
branch. so thats why select
is missing, it is never there in the first place
why not just have the macros directly use macros/richnav
. I never get these aliasing things, so not sure why it has to be in this ns
deftime is just 1 line:
(defmacro deftime
"This block will only be evaluated at the correct time for macro definition, at other times its content
are removed.
For Clojure it always behaves like a `do` block.
For Clojurescript/JVM the block is only visible to Clojure.
For self-hosted Clojurescript the block is only visible when defining macros in the pseudo-namespace."
[& body]
(when #?(:clj (not (:ns &env)) :cljs (re-matches #".*\$macros" (name (ns-name *ns*))))
`(do ~@body)))
I thought so, but defmacro
s defined in a macrovich/deftime
don't appear in JS output produced by a shadow-cljs :bootstrap build.
shadow-cljs already ignores defmacro in .cljc files unless compiling macros, so having them there doesn't do anything "extra"
I understand that I don't need deftime here, but I'd like to understand why it doesn't match my mental model. You said before that deftime sounds like it should be what I want. But nothing that's inside deftime ends up in the JS output. Is that expected, given its definition above?
the defmacro should definitely be in the generated $macros.js file, but not in the regular ns .js file
@U05224H0W https://github.com/shadow-cljs/quickstart-browser/compare/master...mbertheau:quickstart-browser:master
I just noticed a possible misunderstanding. I didn't mean that the deftime macro itself doesn't end up in the JS, but that what's inside a call to the deftime macro doesn't end up in the $macros.js file.
goog.provide('friday.myns$macros');
console.log("hello from toplevel");
friday.myns$macros.toplevelfunction = (function friday$myns$macros$toplevelfunction(){
return console.log("hello from toplevel function");
});
var ret__5824__auto___14156 = friday.myns$macros.toplevelmacro = (function friday$myns$macros$toplevelmacro(_AMPERSAND_form,_AMPERSAND_env){
console.log("hello from toplevelmacro expansion");
return cljs.core.sequence.cljs$core$IFn$_invoke$arity$1(cljs.core.seq(cljs.core.concat.cljs$core$IFn$_invoke$arity$2((new cljs.core.List(null,new cljs.core.Symbol("js","console.log","js/console.log",-2005248266,null),null,(1),null)),(new cljs.core.List(null,"hello from toplevelmacro result evaluation",null,(1),null)))));
});
(friday.myns$macros.toplevelmacro.cljs$lang$macro = true);
console.log("hello from usetime");
friday.myns$macros.usetimefunction = (function friday$myns$macros$usetimefunction(){
return console.log("hello from usetimefunction");
});
var ret__5824__auto___14160 = friday.myns$macros.usetimemacro = (function friday$myns$macros$usetimemacro(_AMPERSAND_form,_AMPERSAND_env){
console.log("hello from usetimemacro expansion");
return cljs.core.sequence.cljs$core$IFn$_invoke$arity$1(cljs.core.seq(cljs.core.concat.cljs$core$IFn$_invoke$arity$2((new cljs.core.List(null,new cljs.core.Symbol("js","console.log","js/console.log",-2005248266,null),null,(1),null)),(new cljs.core.List(null,"hello from usetimemacro result evaluation",null,(1),null)))));
});
(friday.myns$macros.usetimemacro.cljs$lang$macro = true);
//# sourceMappingURL=friday.myns$macros.js.map
Is that a peculiarity of shadow-cljs bootstrapped cljs? shadow-cljs does compile the cljs to js outside the browser. Is that different in this regard compared to compiling all the namespaces in the browser?
I pushed a commit that logs (ns-name *ns*)
in all the places and only ever get null
or markus.pumpkin
, never markus.pumpkin$macros
.
*ns*
is definitely bound to the correct value. most of your additions were incorrect though. I don't understand the problem with deftime either, but I do not see the point anyways so just do not use it
Hello, I think I found a bug. For now I don't have an isolated reproducible setup, but I consistently trigger it on penpot project compilation. In summary: when module split + function tht uses reify. The result is: there are a function1 that uses reify, which will define the class once the function1 is used (defined in module shared.js); and for some reason a one method impl is splitted to the main.js module where the prototype is extended with the missing method, but the object is obviously does not exists until the function1 is called. Code sample from shared.js:
function $promesa$exec$scheduled_executor$cljs$0core$0IFn$0_invoke$0arity$0variadic$$($p__32931$$) {
var $map__32932__$1$$ = $cljs$core$__destructure_map$$($p__32931$$)
, $parallelism$$ = $cljs$core$get$$.$cljs$core$IFn$_invoke$arity$3$($map__32932__$1$$, $cljs$cst$172$parallelism$$, 1)
, $factory$jscomp$2$$ = $cljs$core$get$$.$cljs$core$IFn$_invoke$arity$2$($map__32932__$1$$, $cljs$cst$173$factory$$);
if ("undefined" === typeof $promesa$$ || "undefined" === typeof $promesa$exec$$ || "undefined" === typeof $promesa$exec$t_promesa$0exec32933$$) {
$promesa$exec$t_promesa$0exec32933$$ = function($p__32931$jscomp$1$$, $map__32932$jscomp$1$$, $parallelism$jscomp$1$$, $factory$jscomp$3$$, $meta32934$$) {
this.$p__32931$ = $p__32931$jscomp$1$$;
this.$map__32932$ = $map__32932$jscomp$1$$;
this.$parallelism$ = $parallelism$jscomp$1$$;
this.$factory$ = $factory$jscomp$3$$;
this.$meta32934$ = $meta32934$$;
this.$cljs$lang$protocol_mask$partition0$$ = 393216;
this.$cljs$lang$protocol_mask$partition1$$ = 0;
}
,
$promesa$exec$t_promesa$0exec32933$$.prototype.$cljs$core$IWithMeta$_with_meta$arity$2$ = function($_32935$$, $meta32934__$1$$) {
return new $promesa$exec$t_promesa$0exec32933$$(this.$p__32931$,this.$map__32932$,this.$parallelism$,this.$factory$,$meta32934__$1$$);
}
,
$promesa$exec$t_promesa$0exec32933$$.prototype.$cljs$core$IMeta$_meta$arity$1$ = function() {
return this.$meta32934$;
}
,
$promesa$exec$t_promesa$0exec32933$$.prototype.$promesa$protocols$IScheduler$_schedule_BANG_$arity$3$ = $JSCompiler_stubMethod$$(0),
$promesa$exec$t_promesa$0exec32933$$.$cljs$lang$type$ = !0,
$promesa$exec$t_promesa$0exec32933$$.$cljs$lang$ctorStr$ = "promesa.exec/t_promesa$exec32933",
$promesa$exec$t_promesa$0exec32933$$.$cljs$lang$ctorPrWriter$ = function($writer__5331__auto__$jscomp$87$$) {
return $cljs$core$_write$$($writer__5331__auto__$jscomp$87$$, "promesa.exec/t_promesa$exec32933");
}
;
}
return new $promesa$exec$t_promesa$0exec32933$$($p__32931$$,$map__32932__$1$$,$parallelism$$,$factory$jscomp$2$$,$cljs$core$PersistentArrayMap$EMPTY$$);
}
And a code sample form main.js
$promesa$exec$ScheduledTask$$.prototype.$promesa$protocols$ICancellable$_cancel_BANG_$arity$1$ = $JSCompiler_unstubMethod$$(1, function() {
// ...
});
$promesa$exec$t_promesa$0exec32933$$.prototype.$promesa$protocols$IScheduler$_schedule_BANG_$arity$3$ = $JSCompiler_unstubMethod$$(0, function($G__32936__$jscomp$255$$, $ms$jscomp$4$$, $f$jscomp$369$$) {
var $done$$ = $cljs$core$volatile_BANG_$$(!1)
, $tid$$ = setTimeout(function() {
try {
return $f$jscomp$369$$.$cljs$core$IFn$_invoke$arity$0$ ? $f$jscomp$369$$.$cljs$core$IFn$_invoke$arity$0$() : $f$jscomp$369$$();
} finally {
$cljs$core$_vreset_BANG_$$($done$$, !0);
}
}, $ms$jscomp$4$$);
$G__32936__$jscomp$255$$ = {
done: $done$$,
cancelled: !1,
"cancel-fn": function() {
return clearTimeout($tid$$);
}
};
return $promesa$exec$__GT_ScheduledTask$$.$cljs$core$IFn$_invoke$arity$1$ ? $promesa$exec$__GT_ScheduledTask$$.$cljs$core$IFn$_invoke$arity$1$($G__32936__$jscomp$255$$) : $promesa$exec$__GT_ScheduledTask$$($G__32936__$jscomp$255$$);
});
function $cljs$core$persistent_BANG_$$($tcoll$jscomp$14$$) {
/...
where you can see that $promesa$exec$t_promesa$0exec32933$$
is used in main.js, but this one will only be defined if the $promesa$exec$scheduled_executor$cljs$0core$0IFn$0_invoke$0arity$0variadic$$
called. And this function is not called until long after the first browser load+eval
Is this make sense?I guess I can workaround it by not using reify on promesa library and replace it with a deftype, but still is very strange that a method definition of some reify object (that is conditionally initialized on first call) is relocated to other module and called on module load+eval time