Hacker News new | past | comments | ask | show | jobs | submit login
ClojureScript Is Not An Island: Integrating Node Modules (clojurescript.org)
193 points by swannodette on July 12, 2017 | hide | past | favorite | 42 comments



One thing I really like about Clojure is how the language has basically stayed the same, but I can use it on the heavy server-side stuffs like working with Hadoop, Spark, ElasticSearch to the front-end stuff like React, React Native, to CLR and now into nodejs, while staying sane [1] in the process. It feels like comparing a nice tropical island paradise to a crowded, hectic, mutating market, where everyone is competing for your attention and selling their newest wares.

[1] https://hackernoon.com/how-it-feels-to-learn-javascript-in-2...


This feature is really, really a long time coming. Myself and other ClojureScript core contributors are happy to answer any questions people may have.


It sounds like this handles extern inference if possible. Is that the case? I have wanted to play around with Webrtc for a while now but it basically requires adapter.js (https://github.com/webrtc/adapter) and as a result a ton of externs.

It would be cool if I could just plug and play!


There's no extern inference required here as all the original sources are passed directly through Google Closure Compiler. As a benchmark for feasibility when we started we chose React.js as it's non-trivial and heavily depended upon in the ClojureScript ecosystem. We discovered that over all of React's sources we only need 4 extern definitions for 4 generated names in EventsPlugin. So as long as the JS library in question isn't relying on string-based meta programming everything should work.

Granted this rules out some libraries, there are plenty of libraries written in a simple straightforward style that will work just fine. In particular I am bullish about fantastic results with ES6+ libraries written with ES6 classes with static import/export. Closure will eat that stuff up.


That's a surprisingly positive result. Usually I expect most JavaScript libraries to be destroyed by Closure's Advanced optimizations, unless they've been written specifically with Closure in mind (which they're not).

Anyway, congrats on those features :-) Scala.js doesn't do the part where Closure is applied on JS dependencies; it's only applied to Scala.js code (though with the advantage that we never need any externs).


how does interop works in scalajs without externs?


The Scala.js compiler, thanks to its static types, precisely knows which property accesses refer to "internal" properties (defined in Scala code, which can be renamed) and which ones refer to external JavaScript code (which cannot). It then simply always emit the former using dot notation (foo.bar) and the latter with bracket notation (foo["bar"]). This allows Closure to rename all internal property accesses but not external property accesses.

It's even slightly more powerful than using externs: if you use the same property name both in internal accesses and external ones, Scala.js can rename the former without touching the latter. An externs-based solution won't be allowed to rename the internal accesses.


Just to add to this statement, even in cases where externs will still be necessary, adding CLJSJS packages[1] to your project's classpath will result in those externs being used even if you don't use the libraries that the packages provide.

http://github.com/cljsjs/packages


What were those four names?



This is huge. Very excited for this! Is there support for private NPM repos? NPM looks for the registry URL and auth token in ~/.npmrc and uses that to authenticate.


We shell out to `npm` to install the dependencies you specify, so I don't see why it wouldn't work.


Sweet. Looks like it should work then. The only problem is that private npm repos use a @namespace/package format. I don't think :@namespace/package would be valid. Looks like you support strings for requires. Do you also support strings for :npm-deps?


strings in `:npm-deps` should just work. I'm more concerned about the npm syntax for `@namespace/package@version` which I'm not sure is valid. If it doesn't work though, please open a ticket so we can fix it!


any stats on how many of the top X npm packages get through the Closure compiler unscathed?


This is awesome. CLJS is amazingly close to having as good host-interop with JavaScript as plain Clojure does with Java.

Will be interesting to see how this impacts things like the React Native and Node.js service-side stories.


This is a nice step forward for developer experience. I struggled to get NPM and Clojurescript to play nicely. Ultimately, I had two different build systems at play in one project. (Three if you count Webpack.)

It'll be nice to have such an easy integration.


The thing I like about Clojurescript is the ability to escape the node/NPM ecosystem entirely. It's feature, not a bug, that I don't want to interoperate with node/NPM.


I would say you are in a distinct minority here with that viewpoint :) I've been using / working on ClojureScript since the very beginning and the halfway integration with Node and NPM never seemed particularly desirable to me.


I hear you, David, and completely understand your point of view. And let me take this moment to say how much your work is loved and admired. I agree I am in the minority. And I also understand why people love a language like JavaScript: they love it so much that they routinely compile it back into JavaScript before running it o_0 ;)


This functionality is opt-in, so you can still live on your island if you'd like :-)


As someone looking to get into CLJS, where do I find "native" CLJS libraries in case I wish to avoid using NPM?


Native CLJS libraries are regular JARs that you add to your classpath. Most people publish them to Clojars[1] (which also has Clojure libs). Maven Central may also have some.

[1] http://clojars.org


Clojure Toolbox (https://www.clojure-toolbox.com/) lists libraries for both Clojure and ClojureScript.


What's the best resource for learning CLJS as of mid 2017?


Depends whether you learn best by reading [1], answering some interactive questions/koans [2], or downloading and playing around with a ToDoMVC app example [3][4][5] or a mixture of the above. Feel free to join the #clojurian Slack channel [6] if you need help.

As a beginner, I recommend you looking at the Reagent [7] or Rum [8] React library first, than you can look at something more complex and full-featured like Om [9] later (whose author is also a ClojureScript core contributor @swannodette).

PS: You can also learn by implementing a Dashboard like this [10]. Disclaimer: I am the author of this library.

[1] http://funcool.github.io/clojurescript-unraveled/

[2] http://clojurescriptkoans.com/

[3] http://todomvc.com/examples/reagent/

[4] https://github.com/reagent-project/reagent/tree/master/examp...

[5] https://github.com/gadfly361/cljs-todomvc

[6] http://clojurians.net/

[7] https://reagent-project.github.io/

[8] https://github.com/tonsky/rum

[9] https://github.com/omcljs/om

[10] https://github.com/priornix/antizer


I like small books that build progressively more complex projects (Dave Thomas's Elixir book was good that way, Realm of Racket was my favorite). Short video tutorials are nice as a supplement are nice, too. Daily Drip's elm videos were fantastic.

The #1 thing I'm usually looking for is something that eliminates any time-wasting hassles related to getting set up, and all the major language features in some set of working examples.

After that, I usually write little command line apps, play with graphics a bit and convert some simple games into the language.


Not ClojureScript but Clojure: I really liked www.braveclojure.com. Maybe not technical enough for the most folks in here, but it was a great starter since you jump right into and solve some problems. Still it was not too newbie focused (Write a program which prints Hello World and calculates the sum out of two numbers). Sadly i wasn't able to stick with clojure since i cant wrap my head around jvm and clojure always felt like something which is fun but will not help me in the future job market.


How does Clojurescript lookup for a module you declare?

For example, in https://github.com/omcljs/om/blob/c68e668a73cc534ecfdc71d631... (couldn't think of any Clojurescript library), you have `[om.dom`, `[cljsjs.react`, `[goog.dom`. Where does the compiler look for these things? How does it knows one is a local module and the other an external package?

I need to know this so I can implement it in https://github.com/fiatjaf/module-linker


This is quite a large question to answer.

From viewpoint of compiler, there is no much distinction between local and external packages. Clojure (and so ClojureScript compiler) is built on to of JVM and uses Maven dependencies so that is where most of the dependencies come from. From viewpoint of compiler, all files are in JVM classpath (or in directories specified in Cljs compiler options).

Om declares it's dependencies in project.clj file: https://github.com/omcljs/om/blob/master/project.clj#L13

- om.dom, based on the name, compiler will look for om/dom.cljs/cljc files in classpath, and in this case the file is local to the project

- cljsjs.react is foreign-library (https://clojurescript.org/reference/packaging-foreign-deps) provided by external cljsjs/react package. Compiler indexes all these foreign-libraries at start, so when it encounters require to one of them knows what to do

- goog.dom is Closure module provided by Google Closure library (https://github.com/google/closure-library), which is dependency of ClojureScript. Compiler will look for goog/dom.js file if no .cljs or .cljc by the name is found.


This is a great step for ClojureScript. It's extremely difficult for a compile-to-JS language to bootstrap a large enough ecosystem to stand alone. A good interop story neatly sidesteps that issue.


That's very true, one of the key aspects that I use for picking a language is the huge ecosystem. Enough cannot be mentioned about the ecosystem of libraries and tools. With Clojure, you can leverage it to lift the heavy server-side stuff like Spark, Hadoop, and use the same syntax basically for the browser UI and mobile.


I just want to thank the Clojure(Script) team for such an amazing work. It really is a joy to work in Clojure(Script) and improvements like this make it an even better tool to work with.

Keep up the good work!


How did clojurescript use node modules / APIs before?


Using NPM for client libraries required using Webpack to bundle those things. If you were targeting Node.js then we just didn't support idiomatic usage of those libraries (users could just call require themselves directly).


Do I still need externs?


The whole point of passing everything through Closure is to avoid needing externs. However there are caveats, see me comment above about building React.js directly from sources.


Scary, any newbie will read this and run away.

Is passing through the complexity, pain and all the caveats one can encounter from this really worth it for using Clojurescript? Serious question, now that with have ES6+ and today babel/webpack are solid tools.

I mean from all the modules mess, I didn't get what's the correct way to do javascript interop right now.


Amazing you consider an unstable, undecided, and hectic JavaScript world (language, 3rd party libraries, many competing tools, etc.) against the sanity of a singular language that has been stable and ahead of game since it's creation.


I fail to see how Closure Complier is any more complicated, more painful, and has any more caveats than Babel/Webpack. With Closure Compiler at 8 years old and building some of the most sophisticated JS properties on the web (GDocs anyone?) it seems pretty "solid" to me. Perhaps it not clear from the post but that's a big part of why this works, we're not doing the heavy lifting.


Its not about closure, is all the Clojurescript interop issues/gotchas, what's the correct way to do interop today in Clojurescript?


Nothing has changed about ClojureScript interop in 6 years so I don't really know what you're getting at. We believe ClojureScript interop is pretty good and our users pretty consistently agree.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: