How does cljs read namespaces wrt read-eval? E.g. https://github.com/taoensso/encore/blob/v2.122.0/src/taoensso/encore.cljx#L2709 there is read-eval usage that requires the previous defn to be already compiled and available for execution, but I'd expect that to be impossible during cljs analysis, as cljs->js compilation happens at a later stage
> so the ns is created properly and can use all core things In the old encore version it's not just clojure.core but the vars from the namespace itself as well, as the initial link shows. Thanks @p-himik and @thheller for the investigative work. I thought someone will know the answer off-hand, guess this is a dark alley for everyone
It's a cljx file, I don't even remember anymore how those are supposed to be loaded. :)
But if you try #=(...) in a cljs file, you'll be disappointed because it's evaluated at read time, so within the CLJ context (unless you use self-hosted - no clue how it would work there). And yeah, it wouldn't work. You can't even do (+ 1 2) there, it has to be (clojure.core/+ 1 2).
Just tried out of curiosity.
Couldn't require the namespace due to some other opaque error: Not supported: class clojure.core$print.
But if I remove everything unrelated, an error that I would expect pops up: Unable to resolve symbol: ms in this context.
Maybe it used to be different for some old version of CLJS, no idea.
I'm confused, encore promotes itself as a clojure and clojurescript library, are you claiming the linked version (1.122.0) shouldn't load in clojurescript?
I tried loading that version and it compiled, so I'm not sure what is it that you tried
I was loading it from sources. I'll try to see what's going on once I get home.
Wow, OK. I have absolutely no clue how that works.
Trying with shadow-cljs just because it's easier for me to use.
#=(...) in my own sources, regardless whether it's in cljs or cljc, leads to a syntax error.
#=(...) in taoensso/encore.cljc is compiled just fine, no issues whatsoever.
Hey @thheller, maybe you know?
I wouldn't expect this to work for CLJS compilation no
I suspect that its just read as a normal tagged literal, so with the = tag. and since its in comment it never makes it to actual compilation
definitely no read-eval happening for CLJS
Yeah, but in the most recent Encore build, it's not under comment.
And even if I write my own (comment #=(...)) - it doesn't work at all, still a syntax error.
This is how it looks in Encore:
(defn ms
"Returns ~number of milliseconds in period defined by given args."
{:arglists '([opts] [& {:as opts :keys [years months weeks days hours mins secs msecs ms]}])}
(^long [{:keys [years months weeks days hours mins secs msecs ms]}]
(round0
(+
(if years (* (double years) #=(* 1000 60 60 24 365)) 0.0)
(if months (* (double months) #=(* 1000 60 60 24 29.53)) 0.0)
(if weeks (* (double weeks) #=(* 1000 60 60 24 7)) 0.0)
(if days (* (double days) #=(* 1000 60 60 24)) 0.0)
(if hours (* (double hours) #=(* 1000 60 60)) 0.0)
(if mins (* (double mins) #=(* 1000 60)) 0.0)
(if secs (* (double secs) 1000) 0.0)
(if msecs (double msecs) 0.0)
(if ms (double ms) 0.0))))
It compiles, the ms function works in CLJS.
When I copy this function to my own ns and use round0 from Encore, it fails with a syntax error.hmm that surprises me. (js/console.log #=(* 1000 60 60 24 365)) just works
guess tools.reader does indeed handle it
Does not work for me:
ClojureScript 1.12.42
cljs.user=> (js/console.log #=(* 1000 60 60 24 365))
Syntax error reading source at (REPL:1).
Unable to resolve symbol: * in this context
Syntax error reading source at (REPL:1).
<NO_SOURCE_FILE> [line 1, col 2] Unmatched delimiter ).doesn't work in the REPL no, but source file it does
bit weird since that is supposed to be using the same reader
https://github.com/clojure/tools.reader/blob/master/src/main/clojure/clojure/tools/reader.clj#L880
didn't know that defaults to true and at least shadow-cljs never sets this anywhere at all
It doesn't work for me in a source file as well:
------ ERROR -------------------------------------------------------------------
File: /home/p-himik/tmp/cljs-playground/src/proj/read_eval.cljc:4:41
--------------------------------------------------------------------------------
1 | (ns proj.read-eval
2 | (:require [taoensso.encore :as encore]))
3 |
4 | (js/console.log #=(* 1000 60 60 24 365))
-----------------------------------------------^--------------------------------
Syntax error compiling at (4:20).🤷
I think I'll continue not using #=(...).
if you setup a repro I can take a look, but works fine in my source file
but yeah I never use it either
that is indeed weird. doesn't work for me either
So at least there's no evil spirit. But what's different with your setup where it does work?
I'm trying to figure that out 😛
absolutely no clue. works in shadow-cljs itself just fine, but not other projects
maybe lein/nrepl related
Huh.
But that [not] working in different contexts notwithstanding - how could there possibly be a difference between compiling the #=(...) inside my own proj.core and inside taoensso.encore required by proj.core?
The former doesn't work for me, the latter does. As if .cljc files from jars go through some different reader.
ah found it
ok, so the actual error is
Execution error at clojure.tools.reader/read-eval (reader.clj:596).
Unable to resolve symbol: * in this context
shadow.user=> *e
#error {
:cause "Unable to resolve symbol: * in this context"
:via
[{:type clojure.lang.ExceptionInfo
:message "failed to compile resource: [:shadow.build.classpath/resource \"proj/core.cljs\"]"
:data {:source-excerpt {:start-idx 0, :before ["(ns proj.core)" ""], :line "(js/console.log #=(* 1000 60 60 24 365))", :after ["" "(defn -main [])" ""]}, :file #object[java.io.File 0x714a513d "/Users/thheller/code/tmp/read-eval-in-cljs/src/proj/core.cljs"], :resource-id [:shadow.build.classpath/resource "proj/core.cljs"], :ex-type :reader-exception, :column 40, :line 3, :url #object[java.net.URL 0x226c0457 "file:/Users/thheller/code/tmp/read-eval-in-cljs/src/proj/core.cljs"], :source-id [:shadow.build.classpath/resource "proj/core.cljs"], :tag :shadow.build.compiler/compile-cljs, :ex-data {:type :reader-exception, :line 3, :column 40, :file "proj/core.cljs"}}
:at [shadow.build.compiler$maybe_compile_cljs$fn__15639 invoke "compiler.clj" 1176]}
{:type clojure.lang.ExceptionInfo
:message "Syntax error compiling at (3:19)."
:data {:type :reader-exception, :line 3, :column 40, :file "proj/core.cljs"}
:at [clojure.tools.reader$read_STAR_ invokeStatic "reader.clj" 955]}
{:type clojure.lang.Compiler$CompilerException
:message "Syntax error compiling at (3:19)."
:data #:clojure.error{:phase :compile-syntax-check, :line 3, :column 19, :source "NO_SOURCE_PATH"}
:at [clojure.lang.Compiler analyze "Compiler.java" 7340]}
{:type java.lang.RuntimeException
:message "Unable to resolve symbol: * in this context"
:at [clojure.lang.Util runtimeException "Util.java" 221]}]
:trace
[[clojure.lang.Util runtimeException "Util.java" 221]
[clojure.lang.Compiler resolveIn "Compiler.java" 7944]
[clojure.lang.Compiler resolve "Compiler.java" 7883]
[clojure.lang.Compiler analyzeSymbol "Compiler.java" 7844]
[clojure.lang.Compiler analyze "Compiler.java" 7300]so it works if the namespace in question is also loaded in its CLJ variant, so for a .cljc file if you add (:require-macros [proj.core]) it works just fine
but if it only exists on the CLJS side then the read-eval fails because it doesn't know any of the symbols
kinda makes sense I guess
Ewww. Where's my 20 foot stick. Thanks for figuring it out!
once again proof that shortening error messages and stack traces is a mistake 😛 figured it out instantly with the actual non-pretty-printed error 😛
kinda undefined behavior I guess. no reason to break encore and forcing *read-eval* to false though
Because of the "Unable to resolve symbol: * in this context" part?
I saw it when I tried figuring it out myself, but it didn't make it obvious to me that :require-macros is to blame.
I guess the main issue for me was that "unable to resolve" became "syntax error" - definitely not an intuitive conversion for me. But what "syntax" is by itself is debatable, of course.
read-eval evals in CLJ of course. during compilation *ns* is bound to whatever the current ns is, so say proj.core. cljs during compilation calls (create-ns that-sym) to ensure the namespace exists
but create-ns creates only an empty ns. without any actual refers/requires like the ns macro would
you can try this in the REPL with simply (in-ns 'i.dont.exist)
it'll do the ns switch just fine, but none of the clojure.core things are available in it, so no other eval works, e.g. no (* 1 2)
Ah yeah, once you mentioned :require-macros on self I immediately got it.
yeah once this :require-macros is added the ns is actually required on the CLJ side before the CLJS compilation starts. that require ensures the ns macro does its thing. so the ns is created properly and can use all core things.
yeah bit of a head scratcher
I'm writing https://github.com/noelrivasc/remolino-clj, a library / article on decoupling Tailwind styles from Hiccup structure. There's more README than there's code.
The gist:
• Follow BEM conventions to name classes in hiccup.
• Make a map that links BEM classes to vectors of classes.
• Use a macro to pre-process components, applying the classes.
Me being newish to clj[s], there's a good chance I'm reinventing the wheel or doing something strange.
I appreciate any feedback / constructive criticism gratitude
Edit: This turned out, in fact, to be a case of reinventing the wheel. Feel free to have a look, but I don't suggest you do 🙂
My first thought is that tailwind already supports this out of the box no? You can define new classes that expand to known tailwind classes.
🤔 I'm no Tailwind expert, but https://tailwindcss.com/docs/styling-with-utility-classes#using-components I didn't find anything to do that kind of expansion. If that's possible, then maybe it's the way to go! Do you have a link to that?
Also, something similar could be done with Sass @extend but it can lead to massive lists of class names.
Interesting. The docs for 3.x https://v3.tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply using @apply to compose utility classes. The docs for 4.x omit that and https://tailwindcss.com/docs/styling-with-utility-classes#using-custom-css. Digging a little, I found that using apply https://github.com/tailwindlabs/tailwindcss/discussions/16349#discussioncomment-12097590 in 4.x.
So, that's right! Tailwind's own tools are enough to decouple structure from styles. I'll change the README to suggest that as the first choice; not sure if there's any point for the clj macro now.
Thanks @christian767 for pointing that out.