Fork me on GitHub
#clojurescript
<
2017-06-12
>
qqq00:06:45

@darwin: is it possible to makes oops.core/ ocall oget oset! gcall to become no-ops (so it can compile in cljc files); or does this make no sense, because oops.core should never be used in a cljc file?

danielcompton00:06:43

@qqq seems like a case for reader conditionals?

darwin00:06:04

@qqq I think, nothing is stopping you to wrap oops.core api with your own macros (unless you want to do this for a library code you don’t have control over)

Jon04:06:26

lu=>> lumo
Lumo 1.5.0
ClojureScript 1.9.542
Node.js v7.10.0
 Docs: (doc function-name-here)
       (find-doc "part-of-name-here")
 Source: (source function-name-here)
 Exit: Control+D or :cljs/quit or exit

cljs.user=>
cljs.user=> (defmacro my-if [p t f]
       #_=>   `(if ~p ~t ~f))
#'cljs.user/my-if
cljs.user=> (my-if true 3 2)
(if 2 nil nil)
cljs.user=>

Jon04:06:36

boot.user=> (defmacro my-if [p t f]
       #_=>   `(if ~p ~t ~f))
#'boot.user/my-if
boot.user=>

boot.user=> (my-if true 3 2)
3

anmonteiro04:06:41

@jiyinyiyong macros don’t work the same way in ClojureScript as they work in Clojure. They need to be declared in a different compilation step

Jon04:06:17

Is that mean another file?

Jon04:06:34

I saw that post but never really understand it

anmonteiro04:06:20

not “another file” if you’re at the REPL

anmonteiro04:06:23

but another namespace

Jon04:06:34

thanks, I got it working now, in file. but still see problem in repl:

Jon04:06:39

cljs.user=> (require-macros '[a.b :refer [my-if]])
No such macros namespace: a.b, could not locate a/b.clj or a/b.cljc
	 (new)
	 Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$3 (NO_SOURCE_FILE <embedded>:1937:200)
	 Function.cljs.analyzer.error.cljs$core$IFn$_invoke$arity$3 (NO_SOURCE_FILE <embedded>:2484:92)
	 Function.cljs.analyzer.error.cljs$core$IFn$_invoke$arity$2 (NO_SOURCE_FILE <embedded>:2483:92)
	 (NO_SOURCE_FILE <embedded>:5239:92)
	 Object.lumo.repl.load_other (NO_SOURCE_FILE <embedded>:6123:380)
	 lumo.repl.load (NO_SOURCE_FILE <embedded>:6127:106)
	 Function.cljs.js.require.cljs$core$IFn$_invoke$arity$5 (NO_SOURCE_FILE <embedded>:5229:528)
	 Object.cljs.js.load_macros (NO_SOURCE_FILE <embedded>:5270:223)
	 (NO_SOURCE_FILE <embedded>:5287:405)

qqq04:06:51

how does EPL interact with cljs ? if I take a repo that is under EPL, copy it over into my codebase, and modify the code -- do now I suddenly have to release all my .cljs file because I served the compiled .js output?

Jon04:06:22

I thought I already create namespace with (ns a.b).

anmonteiro04:06:53

@jiyinyiyong so if it’s loaded you don’t need to require it

anmonteiro04:06:03

just call your macro with a.b/...

anmonteiro04:06:25

require will try to load files, and that namespace clearly doesn’t exist in a file

Jon04:06:16

cljs.user=> (ns a.b)
nil
a.b=> (defmacro my-if [p t f] `(if ~p ~t ~f))
#'a.b/my-if
a.b=> (ns cljs.user)
nil
cljs.user=> (a.b/my-if true 2 3)
(if 3 nil nil)
cljs.user=> (require-macros '[a.b :refer [my-if]])
No such macros namespace: a.b, could not locate a/b.clj or a/b.cljc
	 (new)
	 Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$3 (NO_SOURCE_FILE <embedded>:1937:200)
	 Function.cljs.analyzer.error.cljs$core$IFn$_invoke$arity$3 (NO_SOURCE_FILE <embedded>:2484:92)
	 Function.cljs.analyzer.error.cljs$core$IFn$_invoke$arity$2 (NO_SOURCE_FILE <embedded>:2483:92)
	 (NO_SOURCE_FILE <embedded>:5239:92)
	 Object.lumo.repl.load_other (NO_SOURCE_FILE <embedded>:6123:380)
	 lumo.repl.load (NO_SOURCE_FILE <embedded>:6127:106)
	 Function.cljs.js.require.cljs$core$IFn$_invoke$arity$5 (NO_SOURCE_FILE <embedded>:5229:528)
	 Object.cljs.js.load_macros (NO_SOURCE_FILE <embedded>:5270:223)
	 (NO_SOURCE_FILE <embedded>:5287:405)

Jon04:06:42

what's the right way to use macros in a repl?

anmonteiro04:06:18

the bit you’re missing is (ns a.b$macros)

Jon04:06:56

glanced and missed that part

Jon04:06:57

get it working now, thanks

qqq04:06:33

in dealing with cljs callback hell, is it better to (1) push everything into cljs core async, or (2) just ues whatever is easiest, and mix callback-hell with core async ?

deas08:06:26

@thheller @pesterhazy @john This is what I came up with so far. Not quite sure where to next. https://www.contentreich.de/webpack-meets-the-jvm

thheller08:06:08

@deas interesting .. but I’m still not convinced running webpack in the JVM is useful. why not just call node? Unless you want to port npm and everything else as well?

pesterhazy08:06:29

I was going to make the same comment as @thheller

pesterhazy08:06:58

shelling out to node should be more straightforward, no?

deas08:06:44

@pesterhazy @thheller Depends on the amount and type of interop. Not sure shelling out makes sense for frequent incremental recompilation, hot reloading and things along those lines.

deas08:06:05

Other than that, I think pulling it in as a dep is pretty convenient.

pesterhazy08:06:46

shelling out is almost instant with node

thheller08:06:49

I’d rather just yarn add --dev webpack since I’m going to install all other packages that way

thheller08:06:14

I have written entire webpack configs in CLJS though

thheller08:06:28

but converting to JS objs makes that a bit annoying

thheller08:06:51

probably easier to directly call webpack

pesterhazy08:06:59

@deas, using webpack separately is pretty straightfoward, see https://github.com/pesterhazy/double-bundle

thheller08:06:07

shadow-cljs can also emit in webpack compatible style

thheller08:06:17

so you only use webpack to bundle things together

thheller08:06:48

but if you are CLJS first that doesn’t make sense

thheller08:06:11

only useful if you have lots of JS and just want to introduce some CLJS without giving up your entire toolchain

pesterhazy08:06:04

I mean you usually don't have to recompile the js bundle anyway during development unless you change the config

pesterhazy08:06:24

in the typical "double bundle" scenario

thheller08:06:59

yes, double bundle is good. only issue I have with that is that code splitting gets annoying

deas09:06:07

@theller I am not saying you are on the wrong track. I think the process boundary comes with restrictions which just don't need to be there.

deas09:06:58

Family calling, got to go for now...

thheller09:06:39

might be better to just all out replace webpack, it isn’t that great to begin with

pesterhazy09:06:13

what's better? rollup? browserify? rolling your own?

thheller09:06:22

closure compiler …

thheller09:06:47

just need to wait until it supports “more” of that JS mess 😉

pesterhazy09:06:10

agree, that'd be great

pesterhazy09:06:29

but honestly in the meantime, webpack works well

thheller09:06:30

well enough … the code it produces is shit compared to the closure compiler

thheller09:06:50

https://clojurescript.org/guides/javascript-modules the babel transforms section seems promising

thheller09:06:21

first run everything through babel then closure

thheller09:06:37

doesn’t have to be nashorn, could just shell out to node

thheller09:06:00

maybe someday .. for now webpack just works

deas09:06:36

I think webpack is about more than cljs and js. I think things like hot reloading and css, scss also matter a lot.

thheller09:06:50

but I don’t want to sacrifice production quality for a little nicer development experience

thheller09:06:01

but webpack does well enough for most things .. just spoiled by closure

darnok14:06:39

is Clojure reader and ClojureScript reader compatible?

darnok14:06:33

I was thinking about a tool (linter) to find all namespaced keywords in the source and check if there was somewhere called (s/def) for everyone of them. This way we check if some keyword was misspelled or has no spec for it.

pesterhazy14:06:12

for linting you might want to look into joker

pesterhazy14:06:40

it already does a good job with cljs source files and is pretty fast

thheller17:06:42

@darnok https://github.com/clojure/tools.reader is used by CLJS to read all CLJS code, can also read all Clojure.

samueldev17:06:30

I've got a clojurescript project that exposes globals on the window, and a separate clojurescript project that pulls those globals off the window and calls them

samueldev17:06:56

I'm using this as a way to facilitate communication between A and B

samueldev17:06:05

(b is just an "addon" that we want to be optional)

samueldev17:06:29

Project A exposes a function I'll call "foo" that expects a clojure map

samueldev17:06:42

We're logging out the clojure map

samueldev17:06:58

* worth noting that Project A calls the functions directly, it doesn't call it via the exposed global

samueldev17:06:07

Project B can't call it directly, so it has to use the exposed global

samueldev17:06:32

When we log out values provided to foo from project A, they're formatted correctly via devtools - but when we log out values provided to foo from project B, this is the result:

samueldev17:06:12

(the first log message is from A, the 2nd is from B)

samueldev17:06:41

i thought this might be because we aren't doing clj->js in B when we call global.foo, and aren't doing js->clj in A when the function executes

samueldev17:06:45

but that doesn't seem to be the culprit

thheller17:06:12

@samueldev that isn’t a good idea to do in the first place since both A and B will contain their own separate implementation of cljs.core adding a good 20KB+ (gzip’d) to each. same for every other namespace they “share”

thheller17:06:28

a better approach would use :modules

thheller17:06:08

also since closure may optimize each build differently they final variable/property names will be different

thheller17:06:22

in general it is not safe to share instances from one build with another

samueldev17:06:47

that makes sense

thheller17:06:55

or serialize it to EDN/Transit and read it on the other side

thheller18:06:09

that would be safe but much slower than passing the actual instance

samueldev18:06:28

our requirements are to be able to load A, and optionally B, via separate script tags

thheller18:06:36

sounds like :modules, they allow you to code split into many smaller chunks that you can load on-demand

samueldev18:06:51

the other reason that we exposed these via globals @thheller is that B is basically an "addon" of A - where A has a strict definition of what an addon can be

samueldev18:06:59

this is because we are going to allow our customer(s) to build their own addons

samueldev18:06:04

which may or may not be in clojurescript

samueldev18:06:18

so they need to be able to hook into the "ecosystem" of A via an addon they've written in plain-js

samueldev18:06:21

or TS, etc etc

thheller18:06:55

then you’d need to make a formal API and ensure that it doesn’t get renamed/removed by closure (via :externs)

thheller18:06:10

but making the CLJS collections part of that API wouldn’t be safe

thheller18:06:25

unless you also extern those

thheller18:06:55

CLJS really isn’t all that well suited for things like that due to the closure compiler

thheller18:06:09

which wants to optimize your whole program at once not part of it

samueldev18:06:58

@thheller so is it basically that the separate instances of clojurescript

samueldev18:06:13

which, youre right, exist needlessly

samueldev18:06:34

are producing clojure-maps-in-the-form-of-js-objs that are incompatible with one another?

thheller18:06:49

yep, exactly

samueldev18:06:08

okay thanks for aiding in our understanding

thheller18:06:29

closure renames all properties so it may rename something important to cA in one but aX in another

samueldev18:06:02

the good news for us is that this "addon" system is a requirement we're meeting for our JS customers... so they won't be using clojurescript to write addon modules

samueldev18:06:45

so this isn't necessarily a hurdle our customers will face (unless they choose to build modules in cljs, I guess 😉 ) - just one that we're facing internally now

thheller18:06:37

for everything you build you can use :modules

thheller18:06:26

but if you build a JS API anyways a CLJS plugin could just use that? Just don’t pass CLJS objects between API boundaries

samueldev18:06:37

yup that's what we're switching to now

samueldev18:06:50

everything clj->js when talking from B<->A or A<->B

samueldev18:06:56

and each side js->clj respectively on the way in

samueldev18:06:17

none of our data structures will cause problems, its all very simple maps with strings, numbers, arrays

thheller18:06:14

but remember that each CLJS plugin will come with a big size overhead due to including cljs.core

samueldev18:06:29

yup, we're ok with that in this scenario. basically this is an SDK

samueldev18:06:30

and an SDK addon

samueldev18:06:43

the outputted size of the UI project that consumes our SDK is monolithic

samueldev18:06:10

and no one has complained yet about size haha (gzip is good and our customers are all desktop PCs with good connections, etc)

samueldev18:06:50

plus the addons our customers are going to build are going to be plain js

darwin18:06:41

@samueldev here is the devtools code responsible for determining whether a given value should be rendered using clojurescript formatter: https://github.com/binaryage/cljs-devtools/blob/d3af0f855d94d0a2b906b1840b5c7534cedbb768/src/lib/devtools/formatters/helpers.cljs#L51

darwin18:06:05

.cljs$lang$type is likely missing on your project B values

darwin18:06:26

didn’t you compile them in :whitespace or :advanced mode?

samueldev18:06:36

yep, this makes perfect sense now that I see the code @darwin

samueldev18:06:45

:advanced yeah

darwin18:06:04

yep, there are two issues in your case: 1) https://github.com/binaryage/cljs-devtools/blob/master/docs/faq.md#why-custom-formatters-do-not-work-for-advanced-builds 2) even if #1 got fixed, as @thheller already pointed out, using devtools compiled as part of a different build is unlikely to work (due to different renaming of cljs$lang$type for example

darwin18:06:46

you would have to have two devtools, one built with project A, another one with project B, and be careful which to use where

thheller18:06:31

one thing that may actually work are the closure variable/property maps

thheller18:06:10

closure can output maps that keep references from old name -> new name

thheller18:06:21

it can also import those and will use the same name on the next compile

thheller18:06:02

I use that to get better caching results when rebuilding with :modules

thheller18:06:18

could also use it to ensure that one build uses the same name as another I guess

thheller18:06:52

but I’d stick with the safer JS option