Fork me on GitHub
#clojurescript
<
2015-07-06
>
looselytyped12:07:21

I am trying to write a node module in ClojureScript based on this post http://www.matthewstump.com/misc/2012/06/04/writing-nodejs-modules-in-clojurescript/ - I tried the following

;; /src/sample/base.cljs
(ns sample.base
  (:require [cljs.nodejs :as node]))

(defn blargl
  []
  (println "blargl!"))

(node/enable-util-print!)
(set! *main-cli-fn* #())

;; compile it at the command line 
cljsc src '{:optimizations :simple :pretty-print true :target :nodejs}' > lib/sample.js

;; in the newly created lib directory introduce a main.js file that makes the functions available to NodeJs
;; In /lib/main.js

var sample = require("./sample.js")
exports.core = sample.sample.base
Then create a simple package.json with "main": "lib/main.js” and run npm pack. In another project npm install it, and require <module-name . But it turns out that blargl is never visible. The only way I could make this work is by setting the ^:export metadata like so.
(defn ^:export blargl
  []
  (println "blargl!”))
Am I missing something really obvious here?

looselytyped12:07:18

I thought simple optimizations would not require the export metadata. I do notice that with ^:export I see this in the generated sample.js goog.exportSymbol("sample.base.blargl", sample.base.blargl); But that line isn’t there without ^:export

dnolen13:07:31

@looselytyped: I highly recommend taking a look at Mori

dnolen13:07:48

I will add that writing Node modules with ClojureScript doesn’t make that much sense at least to me due to a large standard library that cannot be shared. Writing Node.js applications - yes. Writing Node.js modules - no.

dnolen13:07:56

Unless you’re supplying something like Mori.

looselytyped13:07:52

Thanks for the prompt response @dnolen - I really appreciate it. I will certainly take a look at Mori. Just so I understand completely - > doesn’t make that much sense at least to me due to a large standard library that cannot be shared How do you mean? What library are you speaking of?

dnolen13:07:52

the standard library is nearly 10,000 lines of ClojureScript. If you include all the stuff that people actually use like pprint, set etc. it’s more like 14,000 lines of ClojureScript.

dnolen13:07:31

this code can’t be shared without changing how ClojureScript loads code (via Google Closure) which isn’t going to happen.

looselytyped13:07:06

I see. Thanks for the insight. Again, I really appreciate it. The library I was thinking of writing was a simple wrapper for making calls to an internal REST API - We already have a version of it in Clojure, and figured that we could re-use a lot of the code. Perhaps I should rethink that 😕

dnolen13:07:46

@looselytyped: you can totally reuse that code, just don’t bother with Node modules

dnolen13:07:10

totally unnecessary when targeting Node.js, everything that works for the browser just works

dnolen13:07:37

we treat Node.js like any JavaScript engine, it has no real special treatment beyond a very very small number of things

looselytyped13:07:31

Ah! I see … That makes sense. That’s good to know. Again - thank you! thank you! thank you!

mfikes15:07:49

A survey of where we currently stand with respect to ClojureScript and React Native: http://blog.fikesfarm.com/posts/2015-07-06-clojurescript-with-react-native.html

andrewhr15:07:08

@mfikes: thanks for the writeup, so many things happening and sometimes is a bit rough to keep up with all updates

mfikes15:07:00

@andrewhr: Was becoming challenging for me to recall all of the moving parts!

andrewhr15:07:40

community first world problems simple_smile

mfikes17:07:10

In six weeks, David’s ClojureScript in Action talk will be available online: https://qconnewyork.com/video-schedule

dnolen17:07:14

lol, will probably see EuroClojure video sooner simple_smile

mfikes17:07:19

I strangely find myself wanting a bootleg copy simple_smile

shriphani17:07:18

Hi, I have 2 separate and disjoint (one doesn’t require the other) cljs files (both of them contain ^:exported functions). I notice that the compiled version keeps only 1 of them and I often have to edit the js to make the import work. What am I doing wrong ?

mfikes17:07:29

@shriphani: Not enough info. Maybe post the line containing ^:export for each?

shriphani17:07:38

sure one sec.

shriphani17:07:03

sorry I can’t paste the entire contents there.

shriphani17:07:23

oh wait both files contain a function with the same name. could that be an issue ?

mfikes17:07:35

@shriphani: Are you finding that :advanced is re-writing symbols?

shriphani17:07:17

mfikes: the main_content namespace isn’t present in the final js produced.

mfikes17:07:02

@shriphani: Is it declared (ns main-content)?

shriphani17:07:27

well (ns web-structure.main-content)

shriphani17:07:54

and core is (ns web-structure.core)

mfikes17:07:20

Try :whitespace to see if it appears.

shriphani17:07:56

hm I haven’t had good experiences with :whitespace before.

shriphani17:07:18

got weird 'undefined is not a function’ errors on loading the js.

mfikes17:07:52

Hmm… perhaps something more fundamental is wrong… do you see any compiler warnings when building it?

shriphani17:07:53

Successfully compiled "out-adv/web_structure.min.js" in 2.937 seconds.
Compiling "out/web_structure.js" from ["src-cljs"]...
Successfully compiled "out/web_structure.js" in 0.178 seconds.
Compiling "out-adv/web_structure.min.js" from ["src-cljs"]...
Successfully compiled "out-adv/web_structure.min.js" in 3.007 seconds.

shriphani17:07:01

sorry about the multi-line paste.

mfikes17:07:00

So, if you look in your emitted JS, the symbols from one file appear, but not the other?

dnolen17:07:52

@shriphani: sanity checking question - are these files that you are compiling separately getting loaded into the same JS environment?

shriphani17:07:00

dnolen: well yes.

dnolen17:07:20

@shriphani: why are you doing it that way? There’s really no support for that

shriphani17:07:44

@dnolen: the bit of js is building features for 2 separate ml models.

shriphani17:07:01

I know I need to refactor this a bit

shriphani17:07:19

but I threw it together with some Ctrl-C, Ctrl-V for experiments.

dnolen17:07:31

I would not proceed w/o refactoring

dnolen17:07:35

it’s just not designed to work

dnolen17:07:52

you cannot separately compile and expect these files to communicate

dnolen17:07:55

w/o disastrous results

shriphani17:07:32

disastrous results ?

shriphani17:07:38

so say the fn names don’t conflict.

shriphani17:07:43

is it still a problem ?

dnolen17:07:44

doesn’t matter

dnolen17:07:53

data structures won’t match

dnolen17:07:04

you can’t reliably pass data between the two programs

dnolen17:07:19

anyways just don’t do it

dnolen17:07:21

not supported

shriphani17:07:29

so I must avoid having 2 independent cljs files.

mfikes18:07:15

@shriphani: I believe @dnolen is referring to loading two separate JS files produced by the compiler (via :advanced)

shriphani18:07:29

oh no that isn’t happening.

shriphani18:07:38

I get 1 final js file.

mfikes18:07:51

You are only loading "out-adv/web_structure.min.js” ?

shriphani18:07:57

wow sorry I wasted your time there.

shriphani18:07:06

mfikes: yes.

shriphani18:07:02

so I just touched one of the files.

shriphani18:07:19

and the main_content namespace is nuked from out/web_structure.js

mfikes18:07:19

To keep it simple, I’d do a clean build that only produces the :advanced build.

shriphani18:07:32

which admitted isn’t the advanced output

shriphani18:07:20

actually the :advanced mode contains the other ns.

mfikes18:07:54

If you are using cljsbuild, then lein cljsbuild once xxx where xxx identifies the :advanced build

shriphani18:07:38

mfikes: so basically my beef is only with the :none build

mfikes18:07:07

@shriphani: Ahh. So with :none one of the files is not compiled at all?

shriphani18:07:42

oh no it is compiled

shriphani18:07:45

but not included

mfikes18:07:03

Meaning it is not loaded into your JavaScript environment?

shriphani18:07:17

yeah I can see both ns’s there

shriphani18:07:19

{ goog.require("web_structure.core"); }

shriphani18:07:24

the other ns is not included at all.

mfikes18:07:23

Are you setting web-structure.core to be the main ns in your project.clj?

shriphani18:07:55

that means it ignores the other ns entirely ?

mfikes18:07:03

But it does not require the other namespaces...

mfikes18:07:19

So, you can manually load the other namespaces, I suppose.

shriphani18:07:35

any way to get around this and have both ns’s generate ?

shriphani18:07:43

I mean I can just use the :advanced output

shriphani18:07:51

which contains both the ns's

mfikes18:07:35

You could have a top-level namespace which itself requires all of the namespaces you need loaded.

shriphani18:07:01

hm yeah I’ll just do that.

mfikes18:07:07

@shriphani: FWIW, I run in to this when loading :none into iOS. Nothing magical makes non-required namespaces be loaded on their own. https://github.com/mfikes/shrimp/blob/master/iOS/Shrimp/AppDelegate.m#L31

shriphani18:07:48

yeah I’ll add in a 3rd ns.

shriphani18:07:55

thx for your time. really appreciate it.

mfikes18:07:27

No problem. Hope that helps.

shriphani18:07:03

definitely did.

mfikes18:07:39

I’m curious, one question I don’t know the answer to is whether ^:export prevents DCE in :advanced mode. I would hope so.

mfikes18:07:32

FWIW, for iOS, I never use :main as things presume HTML is at play simple_smile

dnolen18:07:40

@mfikes: it does, ^:export just generates an exportSymbol call for you

dnolen18:07:34

@shriphani: sorry for misunderstanding earlier, was thrown off by the incremental output paste.

shriphani18:07:38

@dnolen: nah I goofed up with my question. Sorry I am a bit of a n00b at this and ask questions poorly.

meow18:07:09

If any cljs developers ran into problems using boot in the past, I'm happy to say that boot-cljs-repl has gotten some loving recently and is working well. If you give it a try and run into any issues please let me know. https://github.com/adzerk-oss/boot-cljs-repl

samueldev18:07:50

is domina the defacto standard for DOM selection / manipulation?

meow18:07:27

@samueldev: well, you've got domina, dommy, Google Closure dom, and the dom capabilities that come with most of the reactive tools out there

meow18:07:02

@samueldev: I'd say you need to start with what your requirements are. For simple stuff, the google closure library is already used by cljs so you get it for free and it has good, basic dom tools. Domina and dommy and the others have slightly different intents so you need to match the one that fits your needs best.

mfikes18:07:55

@shriphani: I’m so looking forward to the next version of ClojureScript: Up and Running. I expect it will be the silver bullet that clarifies everything related cljs for us simple_smile

mfikes18:07:56

Until then, we all have to ask ill formed questions, and stumble around in the dark, or read the source. simple_smile

mfikes19:07:50

@shriphani: I took a stab at adding some copy to the documentation for :main that may have helped. Feel free to revise it if you find things are inaccurate: https://github.com/clojure/clojurescript/wiki/Compiler-Options#main

meow19:07:06

@mfikes: On this page you wrote "There is currently no require function..." but it looks like there is one now, right? Or am I missing something? https://github.com/clojure/clojurescript/wiki/The-REPL-and-Evaluation-Environments

meow20:07:42

cljs.user=> (doc require)
-------------------------
require
([& args])
REPL Special Function
    Loads libs, skipping any that are already loaded. Each argument is
  either a libspec that identifies a lib or a flag that modifies how all the identified
  libs are loaded. Use :require in the ns macro in preference to calling this
  directly.

meow20:07:07

This isn't what I expected. What's the right way to reference the current namespace in a repl:

cljs.user=> (dir cljs.user)
nil
cljs.user=>

mfikes20:07:51

@meow: looks like that was written be Brenton Ashworth back in 2011

meow20:07:03

@mfikes: oops - I saw you had edited it and I actually thought it was fairly new for some reason

meow20:07:15

my bad for not looking at the history

meow20:07:39

does figwheel do an implicit/behind-the-scenes (require '[app.namespace]) thus allowing users of its repl to simply do in-ns 'app.namespace?

dnolen20:07:24

@meow: in-ns works regardless of whether the namespace is loaded or not - it’s a REPL feature not a language one

mfikes20:07:24

@meow: I'd recommend correcting it. That page in particular has some outdated stuff.

dnolen20:07:28

a reminder that this is required reading for all ClojureScript users simple_smile

canweriotnow20:07:35

Fig wheel question… working on a luminous project, it seems no amount of wrap-reload will get my server side code to reload when running under fig wheel. Thoughts?

dnolen20:07:35

@canweriotnow: isn’t figwheel just the REPL bit? What does it have do w/ your server side bits?

canweriotnow20:07:23

That’s what I can’t figure out. It shouldn’t matter. But the handler doesn’t see to be picking up the dev env when I start the whole thing from lein figwheel instead of lein run

canweriotnow20:07:20

I think it's new changes to Luminus I'm not used to, I usually just start with compojure and ring but I was in a hurry simple_smile

canweriotnow20:07:14

When I start with lein run I get figwheel websocket issues... maybe need to just clean my cljs and not use figwheel when I'm messing with my servery bits

canweriotnow20:07:55

oh god, turning off autocrrect in Slack makes life so much more legible

canweriotnow20:07:03

uh, mostly 😉

meow20:07:14

@dnolen: par for the course, I'm confused. Inside a boot-cljs-repl session when I use in-ns to access the namespace of the app that is running in the browser I get the following pseudo-error:

cljs.user=> (in-ns 'app.core)
nil
app.core=> (get-title)
WARNING: Use of undeclared Var app.core/get-title at line 1 <cljs repl>
"Informing v0.1.0"
app.core=>
So I'm able to call my app/core get-title function and have it return a string, but I get those undeclared Var warnings. I thought I had come across information indicating that a require of the namespace was, um, required, but you say otherwise. Hence, my confusion.

mfikes20:07:59

@meow: only puts your REPL into the namespace. It doesn't require it and this doesn't cause analysis

dnolen20:07:06

@canweriotnow: yeah I can’t offer more than you probably want to run figwheel sans the built in webserver, don’t know anything about how to do that bit though

mfikes20:07:52

@meow: Since the symbol is obviously loaded :analyze-path may be of interest

dnolen20:07:19

@meow: try that in a Clojure REPL the result won’t be much different

meow21:07:44

@dnolen: I see. Ok. That's what I thought. And doing a (require '[app.namespace]) which in my example would be (require '[app.core]) is the canonical way to load the symbols into the namespace, right?

meow21:07:42

Wait a minute!

meow21:07:55

I just noticed this at line 1 <cljs repl>

dnolen21:07:24

require isn’t about loading symbols, it literally loads the library

meow21:07:16

What does it mean to be in a namespace? Maybe that's what I'm misunderstanding.

dnolen21:07:36

@meow: recall I said above - it’s a REPL feature

dnolen21:07:45

it just allows your forms entered at the REPL to be subject to the resolution rules present in the namespace as supplied by the ns form there.

dnolen21:07:17

Clojure is a bit more flexible as (in-ns) is a real primitive

dnolen21:07:34

and some of these things only make sense in ClojureScript truly as REPL features

meow21:07:05

@mfikes: I've read your posts and they are great - still trying to get my head around in-ns and analyze-path

mfikes21:07:39

@meow: Here is a mental model: There is the JavaScript environment and then there is what your REPL happens to know about that environment. When namespaces are analyzed, the REPL learns about namespaces and which symbols are in those namespaces: ns-interns works, etc.

dnolen21:07:17

@meow: :analyze-path is for a problem that doesn’t exist in Clojure, because the evaluation environment is Clojure itself and Clojure has ns & var info at runtime.

mfikes21:07:19

in-ns does nothing but switch you to a (presumably existing) namespace

dnolen21:07:43

@meow: but when you connect a ClojureScript REPL to a running ClojureScript compiled application in a browser … how can it know what’s there?

dnolen21:07:51

ClojureScript doesn’t have reified vars or namespace objects.

meow21:07:00

Okay, tell me where I am wrong. A namespace is basically (or literally) a map. Being in a namespace means code that subsequently gets executed is executed in the context of that map. So any symbols are "looked up" in that map. If I'm in a REPL that is connected to a running browser app and I use in-ns to put myself into that namespace, then why do I see errors?

dnolen21:07:22

thus :analyze-path hints the REPL - analyze this stuff so that interactions at the REPL match your expectations

meow21:07:34

now I need to catch up and read what you guys wrote while I was typing... 😉

mfikes21:07:47

@meow: Because the “map" doesn’t exist in a reified form that the REPL can use

mfikes21:07:23

@meow: When you type foo.bar/baz in the REPL, it compiles that to access foo.bar.baz, but the REPL may or may not know that this symbol exists.

mfikes21:07:08

@meow: If if helps you from a Java background, a sort of erasure is going on.

meow21:07:44

@dnolen: and @mfikes: that totally makes sense now

meow21:07:58

I don't have a Java background

meow21:07:30

But I did a lot of Python, including writing a GUI REPL and so I learned a lot of its namespace tricks

meow21:07:54

In Python namespaces are dictionaries - real ones that can be introspected

mfikes21:07:00

@meow: Unfortunately, I don’t know where you would learn the above mental model from stuff in the Wiki or otherwise. (My reaction to learning something new is to try to document it, but I think I learned this one through experience, working on Ambly, etc.)

meow21:07:22

I didn't realize cljs didn't have reified namespaces

dnolen21:07:38

@meow: but it also sounds like you’re just getting started with Clojure as well?

dnolen21:07:58

in Clojure namespaces are reified and are very map-like if not literally maps

dnolen21:07:17

ClojureScript doesn’t reify them or vars for performance and code size reasons.

meow21:07:59

Yeah, I started working with Clojure and ClojureScript on May 15th, so almost 2 months now.

meow21:07:14

I took over maintenance of boot-cljs-repl and am trying to make sure it is working as expected.

meow21:07:43

A lot of details about the repl in clj and cljs make way more sense now. Thank you all for your patience.

mfikes21:07:43

@meow: For me it was around April, but in 2014

mfikes21:07:34

@meow: You asked a great question a few days ago about something that I thought was common knowledge, but was actually documented nowhere in the wiki.

meow21:07:37

I have to say that I'm really enjoying the language and the community. I picked up Python in 1999 and now the Clojure community reminds me of those days.

meow21:07:45

It's kind of funny. The first real app I wrote in Python was a repl (PyCrust) and now here I am learning a new language and one of the first things I do is get involved with the repl side of things.

mfikes21:07:59

One challenge with the documentation is: once you know something, it is hard to see that the documentation is lacking it. But when you don’t know something, its easy to see it is lacking. The sweet spot is when you know enough to feel comfortable contributing to the documentation when you happen to see the missing bits.

mfikes21:07:10

@meow: I can attest (twice over), that writing a ClojureScript REPL is a great way to learn things!

meow21:07:17

@mfikes: That is so true. And once I've gotten over the hump I know I will quickly lose interest in whatever those missing bits were. That's why I try to raise these issues as I find them. I assume that if I'm confused then others must surely also be confused (though I could be wrong about that) and that something needs to be improved and if I don't point it out then it's just making it harder for the next developer that comes along.

mfikes21:07:17

@meow: Yeah, the documentation is only 3/4 the way there (you could argue about what the actual level is).

meow21:07:15

That's why I kept finding this cljs repl stuff so confusing based on my python experience and what I knew about clojure namespaces being maps, just like python's were dictionaries, so I couldn't understand why I was running into this issue in the boot-cljs-repl. But now I know. ClojureScript doesn't have reified namespaces so we have to fake it by giving it hints about what is likely in the namespace.

meow21:07:46

I think David kept thinking "why is this guy so hung up on namespaces?" 😉

mfikes21:07:48

I think the pattern should roughly be this: 1. I don’t understand something and it is not clearly documented 2. I ask for help and the community clarifies it 3. (If I’m somewhat experienced and comfortable) go back and add the missing clarification to the document 4. Go to 1

mfikes21:07:25

@meow: Heh. And we’ll likely go through another round of interesting, perhaps different stuff, related to bootstrapped ClojureScript, and what is and isn’t possible in that platform

dnolen21:07:25

@meow: to be clear Clojure namespaces are not maps

meow21:07:00

Another variation for me was to pester the boot folks with all my problems until @alandipert decided to turn the tables on me by offering to make me a maintainer...

meow21:07:22

@dnolen: I thought I read that they were. Let me check.

dnolen21:07:28

@meow: and the hinting issue is subtler than that. It’s really the remote REPL problem.

dnolen21:07:36

@meow: you can check but I know.

dnolen21:07:39

they aren’t maps.

meow21:07:49

@dnolen: Not that I disagree with you, just want to check where I thought I read that.

dnolen21:07:04

@meow: I don’t know but wherever you read that, it’s not right simple_smile

meow21:07:07

Trust me, I believe you. I do.

meow21:07:57

I might be crazy, but I'm no fool.

meow21:07:33

I believe everything you write and read it very carefully, multiple times. simple_smile

mfikes21:07:09

In Clojure Programming it uses language like “they are dynamic mappings between symbols and either vars or imported Java classes.” (and the same language is used in the page David just linked)

mfikes21:07:43

Oops. Thought David linked this: http://clojure.org/namespaces

mfikes21:07:10

@meow: You scared Alan away.

meow21:07:07

@mfikes: I sure did, didn't I!

meow21:07:33

Well, I can't find where I thought I read that clojure namespaces were implemented as maps and it isn't worth the effort. So basically they are map-like things but not maps. That's cool. And the cljs implementation is different for performance and code size reasons. That's cool. I think my confusion has been solved.

meow22:07:21

Check this out, though. Figwheel does do some work behind the scenes in its repl:

This REPL is a little different than other REPLs in that it has live compile information from the build process. This effectively means that you will not have to call (require or (load-namesapce unless it is a namespace that isn't in your loaded application's required dependencies. In many cases you can just (in-ns 'my.namespace) and everything you need to access will be there already.

meow22:07:47

The section of the figwheel docs about "Writing reloadable code" should be required reading for cljs developers: https://github.com/bhauman/lein-figwheel#writing-reloadable-code

mattly23:07:25

I don’t know about need, but I want one