Fork me on GitHub
#clojurescript
<
2022-10-06
>
sirwobin14:10:50

I've just included prismatic schema in a shadow-cljs project running under node. I've used schema in clj many times with success but I'm struggling to get a coercer to convert a string to a keyword. I'm using schema.coerce/json-coercion-matcher which does recognise string->keyword coercions and I've made sure to :include-macros true when requiring schema.core. Are there any gotchas I should be aware of when using prismatic schema with cljs? Thanks in advance for any advice. 🙂

thheller16:10:09

what is the actual problem you are seeing? do you get an error? unexpected output?

sirwobin18:10:19

I see the following: #schema.utils.ErrorContainer{:error {:policies missing-required-key, "policies" disallowed-key}} Schema is (s/defschema bb {:policies #{s/Str}}) and I'm using a coercer which has worked just fine under the jvm to do the right thing. Was wondering if there's a cljs trick I'm missing.

winsome19:10:22

I'm trying to bundle some static assets into a cljs library that can be used from js-land. There's no classpath, so it's not as simple as just using io/resource. One solution I've read about and would like to try is to use new URL('<filename>', import.meta.url). However, I can't figure out how to translate the import.meta.url syntax into cljs - it's a bit of weird syntax. According to the mdn docs: > The syntax consists of the keyword import, a dot, and the identifier meta. Normally the left-hand side of the dot is the object on which property access is performed, but here import is not really an object. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta

winsome19:10:09

I've tried js/import.meta, but that reports Execution error (ReferenceError) at (<cljs repl>:1). import$ is not defined.

winsome19:10:54

Which may be because I'm trying it from the repl and I think it only works inside a module, but this will be in a ES module.

p-himik19:10:02

So this library will be usable only from JS?

winsome19:10:35

No, it's a cljc module, but the jvm side is easy because we have a classpath.

p-himik19:10:27

And how will you consume the assets within CLJS?

winsome19:10:13

It won't work while hosted in the browser, but in node the plan is to use fs/readFileSync

p-himik19:10:59

If the trouble is only with import being renamed into import$, then you can try using js* - not documented, IIRC, but will emit its string argument as is. I.e. (js* "import.meta.url") will become import.meta.url in the compiled JS.

winsome19:10:13

!!! I think that will work. I'm now getting a proper error: Cannot use 'import.meta' outside a module, but I'm pretty sure that's because I'm working from the repl.

thheller19:10:17

it'll also blow up in release builds processed by the closure compiler

thheller19:10:33

import.meta only works in ESM code

thheller19:10:51

doesn't make sense to use in a node setting anyways

thheller19:10:49

the shadow-cljs command reads a file shipped with the npm package like this

thheller19:10:08

so, basically just a fs/readFileSync

thheller19:10:02

js/__dirname is the special binding to get the dir of the current file (meaning the produced JS file, not the CLJS file)

thheller19:10:25

https://unpkg.com/browse/[email protected]/cli/ this is the contents of the npm package. dist.js is the compiled CLJS output

winsome19:10:03

The problem is when this library is used in another application and is living in node_modules/path/to/my/package/ then I need to be able access the /assets folder of the library even though the relative directory has changed.

winsome19:10:34

I was looking at this thread to come up with the new URL("filename", import.meta.url) solution.

thheller19:10:47

ah, webpack. yeah thats a different story

thheller19:10:56

normally just require works

thheller19:10:19

assuming webpack that is, won't work in node or the REPL

winsome19:10:10

I'm not using webpack, that's just the only place where I could find a discussion on how to do it.

thheller19:10:33

yes, because this is a webpack feature

thheller19:10:41

there is no native support in the browser for this

thheller19:10:02

no matter what. you need something that puts the files out of the npm package into a web accessible location

winsome19:10:34

But I'm not dealing with the browser - as long as Node has support for this ES syntax, which I believe it does, it should work. It's a complicated chain, but this cljc library is included in a cljc application that uses shadow-cljs to produce a browser artifact (using :target :esm), a webworker artifact (using :target :esm), and a node artifact (using :target :node-library), and a jvm artifact.

winsome19:10:11

I may need to change the node artifact build to use :target :esm too, but I've got some time to fiddle.

thheller19:10:14

if its a CLJS lib the story is entirely different and none of the JS information applies

winsome19:10:21

Oh! So, I can just slurp it and be done?

thheller19:10:39

as I just said. you need something that puts the file into a web accessible location

thheller19:10:45

shadow-cljs doesn't support that

thheller19:10:25

what kind of files are you talking about here?

winsome19:10:34

Can you elaborate on what a "web accessible location" means?

thheller19:10:57

> to produce a browser artifact

thheller19:10:03

so the browser needs to be able to access it via HTTP, however you deal with that. eg. your public folder

winsome19:10:52

No, this functionality will not work from the browser, it's server side only. Something like this:

#?(:cljs (if (browser-runtime?)
           (throw (ex-info "Not supported" {:sorry :dude}))
           (read-static-asset))
   :clj (read-static-asset))

thheller19:10:41

I don't know what you are asking anymore. what do you want to do? do you need it accessible in the browser or not?

thheller19:10:06

is the client loading it or is the server providing it?

p-himik19:10:20

According to some previous statements, the library works everywhere, including browsers. The static asset will be used only on the server side - on JVM, on Node via CLJS, on Node via JS.

p-himik19:10:10

Maybe the "on Node via JS" part is incorrect though, since I see that above ESM modules are mentioned only in the context of a browser and a web worker.

winsome19:10:16

Sorry for the confusion - I'm building a nodejs application, that uses this cljc library, which needs to read a giant data file off of the filesystem. I need a way for this cljs asset to be accessible to another cljs application that produces an artifact that runs in Node on the server.

winsome19:10:40

^you got it right, @U2FRKM4TW

winsome19:10:04

I don't think how I'm packaging the browser/webworker artifacts is relevant

thheller19:10:38

ok, the solution is not gonna be fund if the same code runs everywhere

thheller19:10:49

there should be some node specific code that is only actually executed in the node env

thheller19:10:02

if the same code is used for the browser this is not gonna work

winsome19:10:00

Right, the same code will not be used for node and the browser - I've got a :closure-defines that will remove all of the node-specific code during a dead-code-elimination pass.

thheller19:10:33

that doesn't work in this case

thheller19:10:52

also are the library consumers aware of this closure-define?

winsome19:10:57

I see what you mean

winsome20:10:06

Does a runtime check work?

winsome20:10:12

Alright, back to the drawing board. Thanks for walking me through it. 👍

thheller20:10:37

I would recommend writing a clojure implementation in its own namespace

thheller20:10:43

and a node implementation in its own namespace

thheller20:10:04

do not use .cljc

thheller20:10:28

and consumers of the lib just chose what they need

winsome19:10:56

If anybody has worked on including static assets in a cljs-produced npm package, I would love to hear about it : )