Fork me on GitHub
#clojurescript
<
2019-12-05
>
indy11:12:07

Hey All, How do I use the Garden library to generate the CSS and then include it in my project? I understand (css [:h1 {:font-weight "bold"}]) will output the appropriate CSS string, but how do I create a CSS file from this and include it in my ClojureScript project? I'm using shadow-cljs as my build tool

p-himik11:12:24

If you generate CSS strings on the frontend, I think you have to explicitly create a <script> tag dynamically with the desired content and add it to the body. If you generate CSS strings on the backend, you can include them in your HTML, however you serve it. Or you can just cache it and serve as a CSS file.

p-himik11:12:33

shadow-cljs doesn't deal with CSS in any way.

Eamonn Sullivan12:12:48

I'm quite new at this, but bumped up against this recently. The way I solved it was just using plain scss files and have a yarn build:sass script in my package.json that runs something like: yarn sass --load-path node_modules/ sass/main.scss:resources/public/css/main.css Now, in the html file (resources/html, for me) I added the following line somewhere: {% style "/css/main.css" %} Et voila! I don't know about Garden, but presumably it has the equivalent of the sass command somewhere to generate the css. Hope that helps!

p-himik12:12:02

With Garden, the process would be similar iff you have static CSS. If you need to generate some particular CSS during the runtime, you cannot use such approach. All in all, depends on what OP needs, yeah.

indy12:12:32

Thanks a bunch for the input guys, will try to figure out something

celwell15:12:22

@UR71VR71S We're using lein-jsass and it's working well.

Eamonn Sullivan10:12:40

Oo, that does look good. Thanks @U0HJJF9A4!

restenb12:12:56

anybody familiar with promesa?

restenb12:12:25

i recently "upgraded" an old project using promesato it's newest version, but the awaitfunction seems to have been removed

restenb12:12:20

i have code like this (p/let [resp (p/await (send-http-request! ...))] (decode-response resp))that is no longer valid due to p/awaitmissing

Filipe Silva13:12:30

last time this topic came up (and promise usage comes up a fair bit), my take away is that yeah you can just go core-async but the initial macro to do it isn't very accessible to newcomers

restenb13:12:37

i just don't get why p/awaitwas removed

restenb13:12:26

i might as well take the time to rewrite the entire http API to use http-fx with re-frame instead, then these pain points are nicely abstracted away

Filipe Silva14:12:39

I remember someone mentioned it was removed from promesa because there were broken corner cases

Filipe Silva14:12:31

but I don't know the real reason, or have a link to the removal

Ramon Rios16:12:14

Does anyone here used schema before?

Ramon Rios16:12:15

I would like to create one for maps within a map

ec18:12:50

If you are going to use a react component lib with regent, reframe such as blueprintjs , does the constant interop with JS kill the perf and dev experience or converting between cljs and js is fast enough?

celwell18:12:22

I've being happily interop'ing with semantic-ui-react for about a year. In terms of dev experience, a lot of hiccup looking like:

[:> ui/Item {:on-click #(rf/dispatch [:b/nav-product-detail product-idstr])}
  [bc/c-product-logo logo]
  [:> ui/ItemContent
   [:> ui/ItemHeader {:class "shorten"}
    pname]
   [:> ui/ItemExtra {:class "product-tags shorten"}
    [bc/c-categories product]]]]

ec18:12:26

Looks ok, what about data side? Esp. if you have frequent(10hz?) json incoming you have to convert to clojure do sth type then back to json does that have too much overhead? Any numbers I could find?

celwell18:12:47

Many of my components are connected to graphql subscriptions over websockets from hasura passed through re-frame subs to update in realtime. Working well, but I don't have hard data.

ec18:12:42

thx I'll continue with cljs then

🙂 4
p-himik20:12:31

If you encounter performance problems while handling JSON data (or even before that), consider using https://github.com/mfikes/cljs-bean or https://github.com/binaryage/cljs-oops

🌟 4
👍 4
Andrew20:12:55

How can I import a json file in cljs?

p-himik21:12:11

If you want to just embed some static data that's available during compilation, I would just write a Clojure macro.

Andrew21:12:00

I’m very new to clojure. What would that look like, and what’s the advantage of doing it that way? I ended up just doing (def myJson (js/require "../path/to/my.json")) which seems to work just fine for loading it as a js object

p-himik21:12:46

Ah, so your platform is Node.js? At least to my knowledge, that's the only place where js/require works.

p-himik21:12:32

If so, using js/require is completely fine.

Andrew21:12:49

require is usable on almost all JS platforms these days. It isn’t implemented in the browsers but almost all js apps are built using bundlers these days which do support import/require and many other features which are compiled down to browser compatible code. In this case I’m using react-native.

p-himik21:12:21

To be honest, I don't know that much about ES5-style, ES6-style, CommonJS-style imports/requires. Although I would argue that a bundler is not a part of a platform. It can do whatever it wants. In your case, since it works just fine, I would say that using it is just fine as well.

Andrew22:12:55

Out of curiosity, what would using a macro look like? I don’t understand clojure macros well enough to understand why they would be useful for static data.

p-himik22:12:05

Macro expansion is done in Clojure, the expanded code is the ClojureScript code, that is then transpiled into JavaScript for the target platform. Suppose my target platform is browser. Ideally, I want to supply a single JS file that has all of the required information needed to for the app to start. Suppose, that JSON must be part of this information. I would then write a macro that looks something like this, I suppose (not tested):

(defmacro static-json [path]
  (core/list 'js* (slurp path)))
It would be used something like this:
(let [data (static-json "path/to/data.json")]
  (do-something-with data))
During the macro expansion phase, the (static-json ...) part would turn literally to what the JSON file contains. Because of the 'js* used in the macro, it would be treated as a JS statement so ClojureScript won't turn it into a ClojureScript map.

p-himik22:12:00

I've done something like this to turn e.g. a static SVG into a Hiccup data simply because I needed to manipulate it before displaying it.

Ryan Watkins20:12:22

Hey all, I'm having some issues with http libraries in cljs, for some reason I can't access a given endpoint no matter what I do

Ryan Watkins20:12:28

Usually it's a cors error

Ryan Watkins20:12:44

But setting a cors header that might fix it is forbidden

Ryan Watkins20:12:57

And when I access the endpoint from postman or Dev tools it works fine

Ryan Watkins20:12:01

Even with the same headers

Ryan Watkins20:12:05

It's driving me crazy lol

p-himik21:12:27

I assume that's because none of the dev tools actually check for CORS. I don't think there's any reasonable way to make it work in a browser without changing the relevant headers on the server.

Ryan Watkins03:12:35

It's weird because I believe that JavaScripts fetch will get a response but some reason xhrio doesn't like this endpoint / server configuration for those endpoints?

p-himik05:12:16

Seems to me the fecth API works just as expected WRT CORS:

p-himik05:12:21

How did you check it?

Ryan Watkins06:12:27

I think because fetch can set headers xhrio can't?

Ryan Watkins06:12:45

NBA.js works for NBA endpoints essentially but for some reason I can't replicate the same in cljs

p-himik07:12:48

Maybe fetch can do that. But it cannot set Origin, and that is what matters.

p-himik07:12:18

Do you run NBA.js in Node?

p-himik07:12:16

And your ClojureScript code in a browser?

Ryan Watkins07:12:35

Yes I ran NBA.js in node

Ryan Watkins07:12:47

Yes in the browser

Ryan Watkins07:12:19

But in the Dev tools as well when I resend the request with the same headers that the original xhr request uses it returns a correct response, I don't understand why

p-himik07:12:46

Can you paste the exact code that you use in dev tools?

Ryan Watkins07:12:30

Unfortunately not right now but perhaps later

Ryan Watkins10:12:05

For instance in the network tab, I see a given xhr request that my cljs has, if I press (resend request) it'll work?

p-himik11:12:54

I need at least some code to be able to say anything useful.

Ryan Watkins11:12:08

(ns ^:figwheel-hooks tylerherro-io.core
  (:require
   [goog.dom :as gdom]
   [httpurr.client :as http]
   [httpurr.client.xhr :refer [client]]
   [promesa.core :as p]
   [reagent.core :as reagent :refer [atom]]))

(def stat-req-p (http/send! client
                            {:method :get
                             :url ""
                             :headers {}
                             }))

p-himik11:12:33

And what was the code that worked for you in the dev tools?

p-himik11:12:23

Because running a plain fetch on this URL doesn't work for me because of CORS, just as expected.

Ryan Watkins11:12:58

I'll just double check, one moment 😄

Ryan Watkins11:12:01

So in Firefox under the network tab essentially I can look at my xhr requests and resend them.

Ryan Watkins11:12:07

Host: 
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Origin: 
Connection: keep-alive
Referer: 
Pragma: no-cache

Ryan Watkins11:12:11

these are the headers

p-himik11:12:56

Well OK, you did send out the request. But were you able to get the response and access it?

Ryan Watkins11:12:29

@U2FRKM4TW only after a resend via the dev tools, the request made by my cljs app returns nothing essentially

p-himik11:12:31

Also, replaying a network request != sending it from code.

Ryan Watkins11:12:44

I know firefox must be doing something internally that doesn't mirror what CLJS is doing

Ryan Watkins11:12:53

perhaps it's sending with a different origin?

p-himik11:12:47

Can you send me a screenshot that contains the response that you see in the dev tools?

Ryan Watkins11:12:20

the valid response?

p-himik11:12:55

Heh, I can't check anything right now because the URL has completely stopped working for me - it just hangs here and time outs.

Ryan Watkins11:12:10

yeah, without headers etc it'll just hang

Ryan Watkins11:12:15

it's very weird lol

Ryan Watkins11:12:37

tbh the NBA api in general is just tough to work with, I just wanted a toy problem to learn some CLJS haha

Ryan Watkins11:12:54

I thought when my app would start it'd just do a GET to a given API and display the data nicely basically

Ryan Watkins11:12:21

but I guess maybe a given client can't do this, I don't know. I don't know the different between say a client running code and a general browser request

p-himik11:12:53

My current guess is that you can view the response in Firefox, but you cannot actually use it in the code. The browser just won't let you because of CORS. NodeJS doesn't enforce CORS, that's why requests work there.

Ryan Watkins11:12:22

because Node is a server-side language? or is it arbitrary?

Ryan Watkins11:12:35

I guess maybe clojure itself can make the request but not CLJS perse?

Ryan Watkins11:12:47

or does clojure enforce it as well (CORS)?

p-himik11:12:25

Because it's server side, yeah. CLJS is a language, not a platform. You can have a CLJS app that runs on Node for example.

Ryan Watkins11:12:47

@U2FRKM4TW via shadow-cljs? (CLJS on Node)

Ryan Watkins11:12:48

and yh, that's true

Ryan Watkins11:12:04

but say for example, cljs aside, I wanted to write a given clojure api that needed to make a GET request, would that have CORS?

Ryan Watkins11:12:43

I know it's kinda backwards but couldn't I myself have a given endpoint that just goes to that other endpoint with CORS ignored?

p-himik11:12:17

I'm being a bit dishonest here. De facto it's the platform that matters. But nothing prevents a particular language having a CORS check in its standard library (although, you would still be able to just not use the library and issue requests some other way).

Ryan Watkins11:12:32

@U2FRKM4TW gotcha, so in theory I could use some low-level stuff to form a XHR request without a CORS check?

p-himik11:12:34

You could use shadow-cljs for that, sure. Although I don't think it's necessary. You can make any requests from Clojure! So yeah, you can write an endpoint that proxies your requests just to deal with CORS. I'm not sure about low level stuff in browser. I think it would work only if you can open raw socket connections from browser, and I don't think you can.

Ryan Watkins11:12:11

@U2FRKM4TW nice, I guess the proxy endpoint would be the way to go then!

Ryan Watkins11:12:13

sounds like fun!

Ryan Watkins11:12:45

@U2FRKM4TW what's a nice way to go about writing a clj API, the ring library?

p-himik11:12:05

It's the default choice for many, yeah. But if you want to just test your CLJS code, you just need to proxy a request, replace some request headers, and remove a bunch of response headers - you don't need CLJ for this. There should be plenty of already written tools for this. It's fine if you just want to play around with CLJ and learn a bit though.

Ryan Watkins11:12:05

@U2FRKM4TW which tools? I tried modifying headers but somehow google's xhrio library is used by most http libs?

p-himik11:12:23

You could probably use raw Apache or NGINX.

Ryan Watkins12:12:56

@U2FRKM4TW can I use JS libraries in cljs?

p-himik12:12:50

Sure! Consider using shadow-cljs for this since it makes it much easier, especially if you need NPM.

Ryan Watkins06:12:50

so I've decided to use shadow-cljs but where exactly would I write javascript or can I somehow interop with javascript in cljs?

Ryan Watkins06:12:34

Even when I proxy it through cors-anywhere I get "Missing required request header. Must specify one of: origin,x-requested-with"

Ryan Watkins06:12:49

when I try to set origin it says I cannot set a forbidden header

p-himik07:12:37

You cannot do it from the browser alone. You have to use some sort of proxy. cors-anywhere doesn't work probably because the server requires additional headers. Although it's strange that it doesn't detect Origin because it should be the one responsible for CORS as well. Have you tried setting x-requested-with manually? shadow-cljs will not help you with CORS in any way. It's just a build tool. But it's a good one and it makes JS interop much more simpler and robust, especially when you use NPM libraries.

Ryan Watkins07:12:48

yes I tried x-requested-with but I guess https://funcool.github.io/httpurr/latest/ doesn't allow me to set it

p-himik07:12:09

You're trying to solve a problem by changing multiple things at once. Try just using raw JS.

Ryan Watkins07:12:20

yeah I think I should yh

Ryan Watkins07:12:30

raw JS with fetch?

p-himik07:12:00

fetch or XMLHttpRequest.

Ryan Watkins07:12:29

fetch cannot set a origin header for a request?

p-himik07:12:00

As I said - try the other header.

p-himik07:12:11

You cannot set Origin within a browser.

Ryan Watkins07:12:19

for some reason fetch form a OPTIONS request

Ryan Watkins07:12:31

but if I resend in the browser with GET as the method I get a valid JSON response

p-himik07:12:34

OK, let me re-highlight. 🙂 You cannot set Origin.

p-himik07:12:37

The browser can.

p-himik07:12:45

But you don't care, because you cannot use it.

Ryan Watkins07:12:54

I'm not setting Origin

p-himik07:12:28

The browser sets it.

Ryan Watkins07:12:40

oops, I forgot to delete Origin from my fetch request fields lol, one sec

p-himik07:12:42

You also don't set cookie yourself, for example.

Ryan Watkins07:12:21

async function getData(url = '', data = {}) {
  // Default options are marked with *
  const response = await fetch(url, {
    method: 'GET', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, *cors, same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'omit', // include, *same-origin, omit
    headers: {
      'Content-Type': 'application/json',
      'x-requested-with': 'XMLHttpRequest'
    },
    redirect: 'follow', // manual, *follow, error
    referrer: '' // no-referrer, *client
  });
  return await response.json(); // parses JSON response into native JavaScript objects
      }
      getData("")

p-himik07:12:12

If you dispatch a request from some backend platform, likely it won't be in your way and will just send the request as is. Meaning, it will add only the stuff you ask it to add. If you dispatch a request from a not horribly outdated browser, it will do all sorts of stuff for you. Set origin, set cookies, check CORS etc.

Ryan Watkins07:12:39

oh I understand

Ryan Watkins07:12:53

damn HTTP is real complicated lol, so many layers

Ryan Watkins07:12:16

but I did read in MDN that it'll do a preflight OPTIONS request sometimes

Ryan Watkins07:12:23

I guess because it's cross-origin? I don't know

p-himik07:12:29

Ha! That's not even scratching the surface.

Ryan Watkins07:12:40

I guess I should try to make the request from a backend then..

Ryan Watkins07:12:45

uh oh, there's more!

p-himik07:12:32

It's like this technology is vastly important and useful and has been around for ages or something. 🙂

Ryan Watkins07:12:13

but I guess it's the browser that makes it complicated

Ryan Watkins07:12:18

not the protocol perse?

Ryan Watkins07:12:24

I don't mind the complexity as long as it's tractable or can be abstracted nicely

p-himik07:12:32

HTTP protocol has not been of interest in this discussion at all. CORS is a mechanism on top of HTTP. Browsers just follow the rules to implement CORS.

p-himik07:12:59

MDN is your friend. Usually they have really nice documentation.

Ryan Watkins07:12:34

why is it that a server can set headers explicitly but not a client?

p-himik07:12:49

For security reasons. You can use a random website that can be malicious. You usually don't use a random server.

Ryan Watkins07:12:06

Okay, that makes sense.

Ryan Watkins07:12:44

Thanks for the help, I know I am a little slow let's say 😄

p-himik07:12:21

No problem.

Ryan Watkins07:12:44

One day I hope I can have this knowledge but I guess it's years of experience away perhaps

Ryan Watkins07:12:30

I was hoping I could find a clojure job in the future but this is like finding a unicorn perhaps haha

p-himik07:12:34

Not really. It depends on what you end up doing. Or on what you want to do. Knowing HTTP and all of the related stuff maybe useful for some, but it's not that helpful for someone that e.g. writes desktop software.

Ryan Watkins07:12:57

hmm, that's a good point

p-himik07:12:09

Even for web development, the vast majority of what there is to know can be more or less safely skipped simply because "it just works".

Ryan Watkins07:12:45

I guess my toy problem is actually a little advanced in the respect it doesn't just work haha

Ryan Watkins07:12:59

because the NBA API is little more less client friendly?

p-himik07:12:00

Probably. I have never had to deal with CORS at all - I have never needed my frontend to load someone else's resources that have been protected with CORS. Maybe someday I will, I don't know.

Ryan Watkins08:12:21

okay so I got an API that provides a nice response ;D

Ryan Watkins08:12:32

question is, how do I begin deploying something like this to heroku?

p-himik08:12:29

By reading Heroku documentation. 😉 It's all there.