Fork me on GitHub
#clojurescript
<
2017-08-07
>
alex-dixon01:08:01

What's the fastest way to encode to JSON from cljs?

alex-dixon01:08:04

My data contains keywords for keys and I need the ability to customize the output some reader tags because some of the data includes compiled js/Function instances (reader tag #object)

mfikes01:08:54

@alex-dixon If you want flexibility and can arrange an efficient way to create a seq of keyvals, applying js-obj to that seq should be pretty fast. It ends up calling goog.object/create which has a tight JavaScript loop that looks like this, prior to :advanced

for (var i = 0; i < argLength; i += 2) {
    rv[arguments[i]] = arguments[i + 1];
  }

mfikes01:08:47

If your question has to do with encoding to a string,

(.stringify js/JSON #js {:a 1})
seems like it would be pretty fast.

alex-dixon14:08:15

Thank you. I think I do want a string…use case is sending large amounts of arbitrary CLJS over the wire. I was looking into CLJS print functions as I thought this might be more performant if I could specify a a custom writer for #object. The functions may be nested so walking to find them seems like a bad way to go. Was hoping I could come across them in the stream and supply a map representation, or at least something that doesn’t blow up the receiving end. Currently sending as EDN to a Clojure server which can’t read #object. Thinking JSON might be best for the rest of the world to consume and potentially faster anyway.

symfrog10:08:43

Was support for modules with optimizations :none included in the 1.9.854 release? Loading modules with cljs.loader/load is failing with

Error Cannot write ... after document load at goog.writeScriptTag_
when attempting to load a module. The error does not occur with optimizations :advanced.

dnolen11:08:08

@symfrog sounds like something else is wrong with your setup?

dnolen11:08:15

module loading doesn’t use script tags if it can’t

symfrog11:08:38

@dnolen Thanks, I have narrowed it down to a require on cljsjs.react in the module that is being loaded, creating a minimal case at the moment

symfrog12:08:03

@dnolen np, minimal case is now available here: https://github.com/symfrog/cljs-modules-react , running

./scripts/brepl
should bring it up on http://localhost:9000

dnolen12:08:41

@symfrog thanks will take a look later

symfrog13:08:17

@dnolen thanks, I have also added a branch called 'advanced-foreign-order' that attempts to use react-with-addons and results in React DOM being prepended to moduleb.js before React. It can be compiled using

./scripts/release

dnolen13:08:39

@symfrog please make a separate repo for that instead of using branches and I will it fork it for tracking

dnolen13:08:49

conflating issues just makes it harder to remember what I’m supposed to do

dnolen13:08:59

I don’t know when I or anyone else will have time to look at it

dnolen13:08:19

related, adding a short description in a README.md is really a requirement

symfrog13:08:01

@dnolen the react-with-addons minimal case is now available as a separate repo here: https://github.com/symfrog/cljs-modules-react-order

dnolen13:08:36

thanks, I will try to make this more minimal

dnolen13:08:09

i.e. it should be possible to reproduce with :foreign-libs, React doesn’t seem essential to this issue

dnolen13:08:38

if further simplfied then you could open issues in JIRA directly

dnolen13:08:06

but I’ll take a look at these - and open issues w/ minimal cases for you if I can repro

symfrog13:08:00

@dnolen thanks, I have now also added a README.md to the original repo

lwhorton14:08:46

i have a semantics question for the new global-exports: in es6 environments the spec is

import MyThing from 'my-thing'
but in an es5 environment the same require statement would usually be this:
var MyThing = require('my-thing').default
I’m not sure if the second case is actually a verified spec, but in either case how would one configure a global-exports such that consumers could use my-thing/default?

lwhorton14:08:54

an example cljs.edn file might be:

:provides ["my-thing"]
:requires ["some-dep']
:global-exports {my-thing MyThing}
But when I do something like this an try to refer to my-thing/default the default is always nil, and instead I have to (.-default my-thing).

alpox15:08:19

Hi all! I'm totally new to clojure/clojurescript (But i read a bunch about it) And now i tried to make a basic project setup with clojurescript which i can connect with cider in emacs. What i don't understand is that different tutorials use different configurations - One specifies a seperate figwheel.clj file for the configuration options, and another specifies the same options under the :cljsbuild key in project.clj. And yet another specifies both! So i wonder what which of them stands for?

anmonteiro15:08:49

@lwhorton there might be another problem here

anmonteiro15:08:07

What version of CLJS are you using?

anmonteiro15:08:11

TLDR is if you set :language-in :es6 that'll probably work after that commit

anmonteiro15:08:23

Let me know if it doesn't

lilactown16:08:51

i'm getting this warning in my figwheel project:

Cannot infer target type in expression (. cljs.core/PersistentVector -EMPTY-NODE)

lilactown16:08:08

and it's pointing at a function that returns a go block

lwhorton16:08:01

thanks @anmonteiro i’ll check this and get back to you

lilactown16:08:26

OK, updating to CLJS 1.9.854 fixed it...

mathieu16:08:04

I am trying to interface directly with Preact, a bit like what @thheller has done here (https://github.com/thheller/shadow/blob/master/src/main/shadow/react/component.cljs#L368-L405) but simpler… essentially I want to create a component:

create Test extends Component { render() { return …; } }
to render

mathieu16:08:39

my understanding is that I have to create a function, and then use goog.object/extend on it

mathieu16:08:58

is there any simple example code of that? not something that tries to wrap in a framework?

mathieu16:08:51

for now, I have something like:

(defn Test [props state]
  (cljs.core/this-as this (js/preact.Component.call this props state) this))

(goog.object/extend
               (.. Test -prototype) js/preact.Component.prototype
               #js {:render (fn [] "…")})
which is probably very wrong because I don't understand it well

plexus16:08:47

Has anyone seen something like this before? WARNING: No such namespace: goog.math, could not locate goog/math.cljs, goog/math.cljc, or JavaScript source providing "" at line 54 (<-- current version of ClojureScript) WARNING: No such namespace: goog.math, could not locate goog/math.cljs, goog/math.cljc, or Closure namespace "" at line 54 (<-- previous version of ClojureScript)

plexus16:08:28

Closure namespace "" <-- somehow it has no idea anymore how to look up a Closure module/namespace?

plexus17:08:46

yeah sure, I can see that the message is different now, but what is causing it?

plexus17:08:39

I would expect it to say "could not locate JavaScript source providing "goog.math"", but instead it looks for an empty string...

juhoteperi17:08:07

Well, don't know about that, but the problem doesn't seem to be caused by the new version

plexus17:08:36

indeed, it is not

anmonteiro17:08:46

I think the message is technically correct

anmonteiro17:08:57

but it’s definitely not friendly

plexus17:08:14

I'm just curious what could be causing ClojureScript to think that (require 'goog.math) could be found in ""... there seems to be something pretty broken with this setup and I have no idea where to start, but this is at least one symptom

juhoteperi17:08:19

Does this problem happen when calling require from REPL or when requiring from another ns?

plexus17:08:32

it just pops up during initial compilation

juhoteperi17:08:42

(ns ... (:require [goog.math :as math])) works

juhoteperi17:08:49

(require 'goog.math) doesn't

juhoteperi17:08:15

I'm not sure if require is supposed to even work like this, isn't that for REPL use?

plexus17:08:44

yeah, I just wrote that as an example

anmonteiro17:08:05

@plexus sorry I’m confused now

anmonteiro17:08:14

are you having trouble requiring goog.math?

plexus17:08:30

actually, no. It seems to be fixed now. I think it was actually being used without being required. Sorry for the noise.

anmonteiro17:08:30

@plexus OK so the error was expected 🙂

plexus17:08:34

I've been banging my head against this build setup for most of the day and this was one symptom that seemed like it could help to explain things, but it was a red herring

anmonteiro17:08:37

the "" refers to JS modules though

anmonteiro17:08:51

that’s why I said the error message is technically correct

plexus17:08:12

if that message is correct then it is at least very confusing 🙂

anmonteiro17:08:34

yes, not friendly at all

anmonteiro17:08:36

I’ll look into it

anmonteiro17:08:22

^ this fixes it

athomasoriginal18:08:54

I am looking to loop over an array of html elements in clojurescript and add event listeners to each element in the array. What would be the best practice for this in cljs? a doseq?

noisesmith18:08:54

if you don’t need return values, yeah, use doseq or run!, if you do need any return value use reduce

athomasoriginal19:08:47

Cheers, noisesmith!

andrea.crotti20:08:30

is there anything that give two datetimes (like now and a specific date), that would return me the difference in - years, months, days, hours?

andrea.crotti20:08:15

I can probably implement it myself otherwise, but seems like something might be useful to many people

andrea.crotti20:08:24

I'm just trying to implement a simple countdown

juhoteperi20:08:15

@andrea.crotti @noisesmith Joda-time obviously has this built-in but this being Cljs channel this is probably about JS. Goog.date doesn't have this built-in: https://google.github.io/closure-library/api/goog.date.html

juhoteperi20:08:52

@andrea.crotti This is my time-ago component, maybe it is of some help: https://github.com/metosin/komponentit/blob/master/src/cljs/komponentit/timeago.cljs#L10, pretty much it just takes difference of timestamps and divides that

andrea.crotti20:08:31

cljs-time and interval gets me close to what I need

andrea.crotti20:08:53

ah yes that's cool I can still some of that for the rest

mfikes20:08:41

The goog.date.duration namespace looks nice, and it has a format fn:

cljs.user=> (duration/format 1000000000)
"11 days 13 hours 46 minutes"

juhoteperi20:08:02

That's the one unparse-duration calls

juhoteperi20:08:43

And now that I'm looking at goog.date.duration code, I remember it is also what I used before writing that time-ago code, but the formatting wasn't really customizable there

andrea.crotti20:08:05

mm yeah that one is good

andrea.crotti20:08:14

but annoyingly it does all the logic in the format

andrea.crotti20:08:18

so can't really reuse it

andrea.crotti20:08:38

ah wait no sorry cljs-time does it

mfikes20:08:39

Yeah, it would be nice if it returned an object with all the values

andrea.crotti20:08:45

didn't read correctly

andrea.crotti20:08:32

ah no it doesn't take it back

andrea.crotti20:08:46

ok well I have to see if it's more annoying to parse that to the values or to implement it myself

ayidi20:08:56

I've found this library to provide the best flexibility: https://github.com/EvanHahn/HumanizeDuration.js

andrea.crotti21:08:49

ah that's pretty good thanks @ayidi

lwhorton21:08:54

I do enjoy watching the js-closure compiler run over all my node_modules dependencies on compilation thanks to 1.9.854. lots of warnings in some of the libs makes me reconsider my actual dependency on such libs. regardless — i’m actually getting a larger bundle size (1.3mb vs 1.4mb) after using the global-exports from node_modules and closure advanced compilation. What might cause this?

juhoteperi21:08:26

@lwhorton :global-exports is opposite of node_modules

juhoteperi21:08:12

If you use node modules, (:require [react :as react]) requires the React code which Closure has converted from CommonJS module to Closure module

juhoteperi21:08:54

:global-exports is a way for foreign-libs (e.g. browserified JS from Cljsjs) to emulate names that npm packages provide, to ease the transition

juhoteperi21:08:22

So if you have the packages installed to node_modules, you aren't using :global-exports

juhoteperi21:08:06

As for the size, hard to say, on my testing using only React was a bit smaller (5%) and it would make sense that advanced optimization and dead code elimination will make the output smaller, but converting JS code from CommonJS to Closure is quite complicated so it is very possible there are cases where it doesn't work very well

lwhorton21:08:22

how does that tie in to cljsjs? surely the compiler has to read an externs file from somewhere? I see a few more-recent cljsjs packages define two entries in their :foreign-libs where one exports a cljsjs package, and the other entry uses global exports (here for example https://github.com/cljsjs/packages/blob/master/react/resources/react-deps.cljs )

juhoteperi21:08:53

With node modules, using Closure module processing, externs are not always necessary. As both app and library code go through Closure processing, the names are mangled in both cases.

lwhorton21:08:23

oof. of course :doh:

juhoteperi21:08:32

But many libraries (e.g. React) create objects in some dynamic way that Closure can't understand, so externs are still required for those cases (https://github.com/facebook/react/issues/9417)

juhoteperi21:08:07

It is possible to use Cljsjs packages to provide externs even when using node modules, because Cljs compiler will use node modules over foreign-libs.

juhoteperi21:08:25

If you have React installed at node_modules, cljsjs/react JS code doesn't get used

lwhorton21:08:59

but at some point does it slurp up the cljsjs externs file somehow?

juhoteperi21:08:12

Yes, all the externs from all the deps.cljs files are always loaded

lwhorton21:08:33

I see. thanks again for the clarification. Makes sense that there’s a lot of code required to closure-ize non-conformist libs

athomasoriginal22:08:52

If I want to work on a #object[NodeList [object NodeList]], which, to my understanding, has a limited set of core clojure methods that can be applied to it, is creating a protocol the only way to do this? As outlined in this article: http://blog.altometrics.com/index.php/2016/05/02/protocols-in-clojurescript/ My goal is to run a doseq against the result of (def el-keys (.querySelectorAll js/document ".key")) - the result is #object[NodeList [object NodeList]] .

athomasoriginal22:08:15

right now If I run (doseq [el-key el-keys] (p el-key)) I will get the following error in the console: Uncaught Error: [object NodeList] is not ISeqable

noisesmith23:08:01

you'll need to find out what the real api is for getting the next item sequentially, then wrap that in a function that makes an ISeq instance

noisesmith23:08:36

probably the easiest way is to use iterate and take-while if it has the typical sort of getNextItem method

noisesmith23:08:41

or actually repeatedly could be the key - something like (take-while identity (repeatedly #(.nextElement source)))

noisesmith23:08:50

depending on what the method is and how that method api works