This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-04-17
Channels
- # announcements (8)
- # babashka (27)
- # beginners (60)
- # biff (7)
- # calva (3)
- # cider (10)
- # cljs-dev (69)
- # clojure (18)
- # clojure-europe (12)
- # clojure-hungary (1)
- # clojure-korea (2)
- # clojure-new-zealand (12)
- # clojure-nl (1)
- # clojure-norway (80)
- # clojure-uk (9)
- # clojurescript (55)
- # cursive (69)
- # data-science (16)
- # events (5)
- # helix (11)
- # hyperfiddle (23)
- # jobs (1)
- # lsp (5)
- # malli (14)
- # matrix (1)
- # missionary (2)
- # off-topic (40)
- # portal (31)
- # re-frame (17)
- # reitit (11)
- # releases (11)
- # shadow-cljs (4)
- # squint (4)
- # tools-deps (5)
- # yamlscript (4)
Hi, i have a npm package with a top level await
and i have a shadow cljs project setup
i am getting {:line 2, :column 0, :message "await must be inside asynchronous function"}
, the top level await is on line 2 [here](https://www.npmjs.com/package/state-machine-cat?activeTab=code)
wonder if you know how to import such a package to cljs
this is currently not supported. unfortunately it basically requires rewriting the entire npm support handling and loading in shadow-cljs. so anything but trivial
would you recommend a different approach to work with npm packages or maybe i should just fork the package and remove the top level await?
unfortunately there is no easy answer. https://shadow-cljs.github.io/docs/UsersGuide.html#js-provider-external works to some extend but introduces a nasty race condition that is not easily worked arround
basically the problem is that loading is assumed to be sync currently. everything is supplied and ready to go. but the top level await turns it into async code
since you cannot mix sync and async it all has to go async. which is not how things are implemented at all š
with :external
only the JS part turns async, which unfortunately still has the same end effect
you can try random things like delaying the loading of the JS and stuff, but its all very hacky and ugly
especially if the await it does actually takes some time or so, then everything pretty much goes to shit
yeah I have been thinking about this for quite some time. I do have some sort of solution, but basically that means deprecating :target :browser
and moving everyhing to :target :esm
"yeah I have been thinking about this for quite some time. I do have some sort of solution, but basically that means deprecating :target :browser
and moving everyhing to :target :esm"
@U05224H0W Wonder if and when roughly you would work on it.
One reason I'm asking is I'd love to try building a product full time on the CLJS+node+browser stack and NPM packages are important to me. For now, I just need the top level async await support for state-machine-cat library.
I enjoyed so much using Clojure at prev job, and investing in JS+Node stack is less appealing to me.
Depending on how hard it is, I am willing to offer help to get the PR across the finish line. I'm just pretty new to Node and CLJS. Did do some CLJ before though.
In any case, thanks so much for shawdow cljs!
It is a fairly significant task, for which I currently do not have enough time to even start. Also not something someone unfamiliar with the shadow-cljs internals could just start doing, unless you want to do a deep dive there š
Also so far the only package I have heard of doing this is yours. It is not a common thing packages do, so doesn't seem super high priority to me.
you could try doing :target :esm
build with :js-provider :import
and post processing the shadow-cljs output via webpack or so
bit annoying and limiting running two different tools, but if you must use that package you are going to make sacrifices either way š
Is there a nice way to generate .css
and .js
files in the same directory as a Clojure file?
I'm trying to write a UI library that uses Clojure/Script to generate CSS (with https://github.com/noprompt/garden) and JS for use in React, Vue, Svelte, Solid, etc. components. The project structure would ideally look something like this:
The hope is that using a single programming language to define both styles and scripts will:
ā¢ Let us re-use values and functions for both static (i.e. static .css
files) and dynamic (i.e. runtime CSS-in-JS) styling.
ā¦ No need to implement, for example, a type scale function in both CSS (or some limited CSS pre-processing language like SCSS/SASS) and JavaScript.
ā¢ Simplify pruning unused values.
For example, suppose we have a type scale function in ui_library/src/lib/type_scale/script.cljc
that returns a font size given an integer (0, 1, ..., 6 corresponds to ,
, ...,
).
We'd be able to use this function in the h1 component's
style.cljc
to generate a static .css
file or the button component's script.cljc
whose generated script.js
is imported into its component.vue
to do runtime CSS-in-JS styling. A Vue/Svelte component (just for readability. React, Solid, etc. would look similar) using both generated CSS and JS would look like this:
I tried using shadow-cljs with its default build targets like :browser
, :node-library
, and :npm-module
and the output directory set to src/lib
but it seems to just output a bunch of JS files directly in src/lib
for both my own Clojure files and any libraries I'm using (e.g. garden). The shadow-cljs.edn
looks like this in all cases but with the target options swapped accordingly:
shadow-cljs isn't a hard requirement. It just seemed like the easiest way to integrate Clojure/Script with an npm package. If this is easier to do with tools.build or Leiningen, that works as well.
no clue about the css garden parts, but for shadow-cljs yes everything only goes into the output-dir, there is no supported option to move them somewhere else
but since its in a predictable directory why not just import * from "./../../script.js";
or whatever instead of import * from "./script.js";
?
:target :esm
might be the best fit, but that will currently be a bit manual since it doesn't have a per-namespace file, rather only the ones configured
and most importantly never mix output files with source files IMHO that gets way too messy in the long run š
I'd like to keep them separate too. Unfortunately, npm doesn't seem to make it easy to have generated source closures that you can just reference with a namespace path regardless of where the actual files are (i.e. the JVM land way with meta-programming).
At least for the scripts, I can make use of Vite's alias feature to map something like $generated
to the shadow-cljs output directory.
Guess the last question is how to evaluate the Clojure code to emit CSS files somewhere as a build step.
fwiw npm doesn't care about .cljs files either. so no point in keeping the src
folder at all, only need the output files
That will probably have to stick around just for the framework-specific stuff. I can probably organize the Clojure source under its own folder in src
though (e.g. use the normal src/main
).
cljs doesn't care where it lives, so just move it into cljs/src
or something. just way less of a headache to put it somewhere svelte/npm can't see
Is there something I'm missing that's causing :target :esm
with module splitting to not produce any build outputs?
Tried following the https://shadow-cljs.github.io/docs/UsersGuide.html#_module_splitting closely but it seems like I messed something up. Not using module splitting works fine.
Minimal reproduction repo: https://github.com/Aokp82ES/test-cljs-emmy-garden/tree/c0ae9229685c9c81b2b0451d2c7fb942b11647a4
[:main] Compiling ...
Module Entry "util.scale" was moved out of module ":util".
It was moved to ":base" and used by #{:css :util}.
currently the way this works is that :exports
turns into :entries
. and :entries
tell the compiler "this namespace must be in this module"
but since the :css
module also wants to use that namespace it breaks since a namespace can only be in one module
so this will not work. it also sort of looks like this split is only done because of this JS cancer of "one function per file"
modules are intented to split out functionality that other modules do not need. if they need it they need to :depends-on
it
This is just a minimal setup right now so it unintentionally has 1 function per file, but I'll be adding more later.