Fork me on GitHub
#clojurescript
<
2021-03-16
>
Audy01:03:23

Anyone familiar with the cljs-ajax library? Is there a way to set the request content-type to text/csv

emccue01:03:33

Add a header with {"Content-Type" "text/csv"}

emccue01:03:42

or {"Accept" "text/csv"}

emccue01:03:48

depending on the api

Audy01:03:40

I went down a rabbit hole and to change the request Header’s content type, it’s under format

Audy01:03:20

The default is transit and was able to change it to text/plain, but not csv

Audy04:03:55

I found a way of doing it, not sure if its good, but it works. pass in a valid text-request-format to :format

:format {:write #(str %)
         :content-type "text/csv; charset=utf-8")}
This will pass in "text/csv; charset=utf-8 in the request header’s content-type

tomrbowden07:03:21

Is there a good online resource detailing the evolution of ClojureScript since it was created? I can’t find a changelog on the ClojureScript github repo.

tomrbowden07:03:10

Thanks @U2FRKM4TW - I must have missed that.

zendevil.eth10:03:02

I have the following code:

(let [r (t/reader :json)
         res->js (clj->js res)]
     (js/console.log "res->js is " (clj->js res))
     (js/console.log "response is " (t/read r res->js))
     )
basically the res variable is a json response from a server. I convert it into js using (clj->js) and this is the output that I get:
{"predictions": [{"description": "Sydney NSW, Australia", "matched_substrings": [Array], "place_id": "ChIJP3Sa8ziYEmsRUKgyFmh9AQM", "reference": "ChIJP3Sa8ziYEmsRUKgyFmh9AQM", "structured_formatting": [Object], "terms": [Array], "types": [Array]}, {"description": "San Francisco, CA, USA", "matched_substrings": [Array], "place_id": "ChIJIQBpAG2ahYAR_6128GcTUEo", "reference": "ChIJIQBpAG2ahYAR_6128GcTUEo", "structured_formatting": [Object], "terms": [Array], "types": [Array]}, {"description": "Seattle, WA, USA", "matched_substrings": [Array], "place_id": "ChIJVTPokywQkFQRmtVEaUZlJRA", "reference": "ChIJVTPokywQkFQRmtVEaUZlJRA", "structured_formatting": [Object], "terms": [Array], "types": [Array]}, {"description": "Surat, Gujarat, India", "matched_substrings": [Array], "place_id": "ChIJYxUdQVlO4DsRQrA4CSlYRf4", "reference": "ChIJYxUdQVlO4DsRQrA4CSlYRf4", "structured_formatting": [Object], "terms": [Array], "types": [Array]}, {"description": "San Antonio, TX, USA", "matched_substrings": [Array], "place_id": "ChIJrw7QBK9YXIYRvBagEDvhVgg", "reference": "ChIJrw7QBK9YXIYRvBagEDvhVgg", "structured_formatting": [Object], "terms": [Array], "types": [Array]}], "status": "OK"}
So I’m using the cognitect transit library to parse this json into clojurescript. And I do it using
(t/read r res->js)
However, I’m getting this error on this line:
SyntaxError: JSON Parse error: Unexpected identifier "object"
How to fix this?

thheller10:03:01

that is not how transit works. t/read can only be used to read the output produced by t/write. clj->js already gave you the CLJ datastructures

thheller11:03:19

eh sorry js->clj will give you the CLJS data.

zendevil.eth11:03:53

@thheller this is what I get when I use clj->js:

{"predictions": [{"description": "San Francisco, CA, USA", "matched_substrings": [Array], "place_id": "ChIJIQBpAG2ahYAR_6128GcTUEo", "reference": "ChIJIQBpAG2ahYAR_6128GcTUEo", "structured_formatting": [Object], "terms": [Array], "types": [Array]}, {"description": "San Antonio, TX, USA", "matched_substrings": [Array], "place_id": "ChIJrw7QBK9YXIYRvBagEDvhVgg", "reference": "ChIJrw7QBK9YXIYRvBagEDvhVgg", "structured_formatting": [Object], "terms": [Array], "types": [Array]}, {"description": "San Diego, CA, USA", "matched_substrings": [Array], "place_id": "ChIJSx6SrQ9T2YARed8V_f0hOg0", "reference": "ChIJSx6SrQ9T2YARed8V_f0hOg0", "structured_formatting": [Object], "terms": [Array], "types": [Array]}, {"description": "São Paulo, State of São Paulo, Brazil", "matched_substrings": [Array], "place_id": "ChIJ0WGkg4FEzpQRrlsz_whLqZs", "reference": "ChIJ0WGkg4FEzpQRrlsz_whLqZs", "structured_formatting": [Object], "terms": [Array], "types": [Array]}, {"description": "Salt Lake City, UT, USA", "matched_substrings": [Array], "place_id": "ChIJ7THRiJQ9UocRyjFNSKC3U1s", "reference": "ChIJ7THRiJQ9UocRyjFNSKC3U1s", "structured_formatting": [Object], "terms": [Array], "types": [Array]}], "status": "OK"}
This is what I get when I use (js->clj):
{"__hash": null, "arr": ["predictions", {"__hash": null, "cljs$lang$protocol_mask$partition0$": 167666463, "cljs$lang$protocol_mask$partition1$": 139268, "cnt": 5, "meta": null, "root": [Object], "shift": 5, "tail": [Array]}, "status", "OK"], "cljs$lang$protocol_mask$partition0$": 16647951, "cljs$lang$protocol_mask$partition1$": 139268, "cnt": 2, "meta": null}
I don’t know what to do with the latter

thheller11:03:40

the js->clj is the proper CLJS datastructure. it just looks that way when printed in the console

thheller11:03:17

you can use (js/console.log (-> res js->clj pr-str))

zendevil.eth11:03:26

in fact just (pr-str res) works

thheller11:03:55

I assumed res was your JSON, if its already converted then pr-str alone is fine

zendevil.eth11:03:48

i don’t know how it was converted automatically. I’m making an http-xhrio request with the response format set to json-response-format. Not sure why it’s automatically converted

thheller11:03:12

yes, I believe that already takes care of the conversion

dnolen12:03:13

@witek that seems pretty strange that = would return anything other than true or false - I agree some minimal example would be useful here to uncover a bug if there is one

dnolen12:03:56

= internally uses identical? so it should have worked. also note that ClojureScript itself uses = in many situations and we don't have this problem you've observed.

witek13:03:31

This is my code:

(fn [^js user]
   (js/console.log "debug-1" user)
   (js/console.log "debug-2" @AUTH_USER)
   (js/console.log "debug-3" (= user @AUTH_USER))
   (js/console.log "debug-4" (identical? user @AUTH_USER))
This is the output:
debug-1 hh {J: Array(0), l: "AIzaSyA0OD1u2f_weTWD7NXLacez7YdbOCjlbpo", m: "[DEFAULT]", s: "", a: Fa, …}
debug-2 null
debug-3 de {a: 0, i: undefined, c: de, b: null, f: null, …}
debug-4 false
And this is the JavaScript:
function(f){
  console.log("debug-1",f);
  console.log("debug-2",u(PR));
  console.log("debug-3",A.c(f,u(PR)));
  console.log("debug-4",f===u(PR));

dnolen13:03:58

@witek it's hard to make sense of that output

dnolen14:03:12

because why are two things printed in the first line for user?

thheller14:03:40

I think thats just one thing with hh or de being the class/type name which the console prints first

witek14:03:10

@dnolen This is copy-paste from Chrome Console. Chrome prints hh and then the "content" of it, which I can expand and so on...

thheller14:03:31

you might be hitting the same kind of issue that clj->js can sometimes hit where it can report false positives for certain "fast-path" protocols

thheller14:03:55

ie. the :advanced renamed the protocol mask property to J and your JS object having a .J property as well

thheller14:03:41

@witek does the issue go away if you compile with :pseudo-names true or :simple?

witek14:03:20

I don't have this problem in development. It is only in advanced.

dnolen14:03:37

@witek yes, but @thheller is explaining the problem

dnolen14:03:49

it looks like you're using some 3rd party lib which might be minified

dnolen14:03:59

and it's clashing w/ Closure's minification

dnolen14:03:46

I'm curious as to why user has collapsed property names

dnolen14:03:16

(this would explain why I felt like this didn't have anything to do w/ =)

witek14:03:58

Yes, I am using other minified .js files. user for example comes from Google Firebase Authentication library which is included via <script src=.

witek14:03:07

Is there a way I can avoid collisions with other minified libs?

dpsutton14:03:21

You can verify that its name collisions with the suggestions > :pseudo-names true or :simple? and if so, you can set a prefix for the renaming https://clojurescript.org/reference/compiler-options#rename-prefix

thheller14:03:23

typically externs take care of this but that doesn't really apply to this issue. kinda tricky

thheller14:03:47

@dpsutton that only prefixes global variables it creates. not property names.

thheller14:03:00

@witek try adding a log for (js/console.log "debug-5" (-equiv user "oops")). just to confirm it is actually hitting that protocol issue

witek14:03:29

--config-merge '{:compiler-options {:pseudo-names true}}' fixes the problem

thheller14:03:49

--pseudo-names (or --debug) is the shortcut for that btw 😉

👍 3
witek14:03:51

(js/console.log "debug-5" (-equiv user "oops")) prints debug-5 de {a: 0, i: undefined, c: de, b: null, f: null, …}

thheller14:03:50

ok yeah that sort of confirms it. only workaround I can think of is adding cljs$core$IEquiv$_equiv$arity$2 to your externs. eg. externs/<build-name>.txt in your project with just one line cljs$core$IEquiv$_equiv$arity$2

p-himik14:03:35

Oh maybe there's a possibility to include Google Firebase Authentication library as an NPM dependency and not via <script>. This should help as well, right?

thheller14:03:50

no that won't change anything

p-himik14:03:31

Even if the NPM sources aren't minified?

thheller14:03:07

well yes that would help but I believe all the firebase stuff is minified

thheller14:03:11

the issue likely is that closure renames cljs$core$IEquiv$_equiv$arity$2 to something short like J

thheller14:03:31

so the code will end up checking if (x.J) { x.J(y) }

thheller14:03:07

so it just blindly calls that function which happens to not be the expected protocol impl but some other function that just happens to return a true-ish value

thheller14:03:56

adding the externs should confirm this is actually what is happening. kinda hard to reproduce since it must hit exactly that renamed property name 😛

witek14:03:59

externs file fixes the problem

witek14:03:12

Is this somehow specific to -equiv ? Or could other functions also run into the same problem?

thheller14:03:53

hmm doh. technically this can hit other protocols as well but requires very specific circumstances

p-himik14:03:17

But it seems that if both compilers/minimizers start their identifiers from a to Z to A to Z then there will be quite a number of name clashes and such issues.

dnolen14:03:20

@witek not specific to -equiv

dnolen14:03:27

but it's not even specific to ClojureScript

dnolen14:03:05

separate minification can always lead to this problem

dnolen14:03:15

any code that checks for a specific object property could be a point of clashing

witek14:03:12

clojure -M:shadow-cljs release spa --config-merge '{:compiler-options {:rename-prefix "happygast"}}' did not solve the problem. Did I something wrong?

dnolen14:03:37

:rename-prefix is not the answer

dnolen14:03:54

if the externs file works for you I would do that

p-himik14:03:32

Hypothetically speaking - is it possible to completely isolate any interaction with such scripts so they cannot at all clash with anything? I assume using them from web workers would be one way.

thheller14:03:41

not really no. you are interacting with a foreign object that would normally require externs

thheller14:03:15

so you could also create externs that "ban" the shortened names that a likely to clash. a-zA-Z or so might be enough

thheller14:03:40

so the spa code wouldn't use those names and never clash

p-himik14:03:10

Oh, but what if you create a non-minifed small JS wrapper to be used along with the offending script in a web worker?

thheller14:03:49

well yes but that is going to be bit extreme 😛

👍 3
witek14:03:36

Would be nice to have a tool in the build pipeline which takes all minified .js files and errors when there is a clash. Would this be somehow possible?

thheller14:03:48

this is an extremely uncommon issue and very hard to hit. pretty much only happens because you basically have two :advanced compiled scripts on the page

thheller14:03:41

well this is what externs are for

thheller14:03:19

a tool would likely generate very many false positives

p-himik14:03:14

I imagine another potential way to prevent such issues would be to never call any CLJS code on the objects returned by the code from minimized third-party scripts. So the example with user above would first use regular JS code (or careful CLJS/JS interop) to create a plain JS object with all the necessary fields, and only then use that object in the CLJS code.

dnolen14:03:02

this used to be a very common problem before people built JS

dnolen14:03:21

directly including separately minified JS into the page via script tags would lead to this problem

dnolen14:03:45

anyways there's nothing to see here 🙂 there's no magic fix

witek15:03:24

So converting objects from minified libraries like user to a clojure map before using it would be a safe approach?

dnolen15:03:30

I would not do anything defensive - some foreign libraries like this simply need externs

dnolen15:03:01

it's painful to run into - but it's outside of our control

witek15:03:51

Thank you all very much! 🙏

p-himik15:03:12

If there supposed to be a field that's not public but on an object from a public API - should externs also include that private field as well?

dnolen15:03:20

@private is supported annotation in externs file - so I would say yes

👍 3