ANN: ClojureScript 0.0-2719, JavaScript Dependencies

784 views
Skip to first unread message

David Nolen

unread,
Jan 24, 2015, 10:10:12 AM1/24/15
to clojure, clojur...@googlegroups.com
ClojureScript, the Clojure compiler that emits JavaScript source code.


New release version: 0.0-2719

Leiningen dependency information:

    [org.clojure/clojurescript "0.0-2719"]

ClojureScript is not an island, like Clojure on the JVM, ClojureScript
embraces the many benefits provided by the host. However this goal
has been hampered by another goal - the compilation stragey. Google
Closure Compiler offers superior optimization and minification for
ClojureScript while simultaneously making it considerably more
difficult to integrate non-Closure compatible libraries. Using popular
libraries like jQuery, React or D3 is an error prone process: "which
extern did I forget?", "which script tag did I miss for development?",
"are these script tags in dependency order?".

No more. 0.0-2719 delivers full support for non-Closure compatible
libraries through some less known features that have been lurking
around for almost three years - `deps.cljs` and the :foreign-libs
compiler option.

`deps.cljs` is a simple EDN file provided at the root of a JAR that
describes additional build information for the ClojureScript
compiler. For example here is the `deps.cljs` for the React JAR I
maintain:

{
 :foreign-libs [{:file     "react/react.js"
                 :file-min "react/react.min.js"
                 :provides ["com.facebook.React"]}
                {:file     "react/react_with_addons.js"
                 :file-min "react/react_with_addons.min.js"
                 :provides ["com.facebook.ReactWithAddons"]}]
 :externs ["react/externs/react.js"]
}

This file provides all the information ClojureScript needs to
correctly manage the foreign dependency for you under all compilation
modes and REPLs.

In a REPL:

   cljs.user> (require 'com.facebook.React)
   cljs.user> (. js/React 
                (renderToString 
                  (. js/React (DOM.div nil "Hello!"))))

In your project:

   (ns foo.bar
     (:require com.facebook.React))

   (enable-console-print!)

   (println
     (. js/React
       (renderToString
         (. js/React (DOM.div nil "Hello!")))))

The above works under all compilation modes. There is no need to
include React as a script tag under development, there is no need to put
script tags in dependency order, there is no need to add React to
:preamble under :advanced, and there is no need to explicitly provide
:externs.

All that is required is that JavaScript libraries be packaged in JARs
with a `deps.cljs`.

Some of you might reasonably ask why not a tool like Bower for this
instead?  Bower requires an additional dependency on Node.js. While
ClojureScript embraces Node.js as a useful target it is not a
requirement to be productive. Bower manages dependencies, but in the
Clojure world we have already embraced Maven for this task and have
done the same for ClojureScript. Finally Bower does not address the
problem of loading libraries at runtime. This is challenging to do -
some JavaScript libraries adopt CommonJS, some AMD, and the most
popular ones make no assumptions at all allowing users to simply use
script tags as they have done for nearly two decades. The above
solution addresses the issue for all ClojureScript users by
eliminating paralysis of choice. Finally none of the above
precludes Bower usage in any way whatsoever.

This new feature addresses a long outstanding pain point with
ClojureScript development. All that is required is that we take the
time to properly package up the most popular JavaScript libraries that
fill gaps not currently served by exising ClojureScript and Google
Closure Library functionality.

Please kick the tires and feedback is most welcome. This feature
touched many bits of code so there are likely to be wrinkles and we want
to get these ironed out as quickly as possible.

## 0.0-2719

### Changes
* Full support for foreign dependencies
* CLJS-985: make ex-info not lose stack information
* CLJS-984: Update Node.js REPL support to use public API
* CLJS-963: do not bother computing goog/dep.js under :none

### Fixes
* CLJS-982: Var derefing should respect Clojure semantics
* CLJS-980: ClojureScript REPL stacktraces overrun prompt in many cases
* CLJS-979: ClojureScript REPL needs error handling for the special functions
* CLJS-971: :reload should work for require-macros special fn
* CLJS-980: ClojureScript REPL stacktraces overrun prompt in many cases
* CLJS-979: ClojureScript REPL needs error handling for the special functions
* CLJS-971: :reload should work for require-macros special fn
* CLJS-936: Multi arity bitwise operators
* CLJS-962: fix inconsistent hashing of empty collections

Bruce Hauman

unread,
Jan 24, 2015, 10:38:43 AM1/24/15
to clo...@googlegroups.com, clojur...@googlegroups.com
Great stuff! This will smooth things out quite a bit. 

I spent far too much time just yesterday trying to get an advanced build working right.

Thanks!


Martin Klepsch

unread,
Jan 24, 2015, 10:43:35 AM1/24/15
to clo...@googlegroups.com, clojur...@googlegroups.com
David, thanks for making those improvements to Clojurescript, they'll
make things a lot easier in the future.

In the realm of Boot we started working on CLJSJS as an effort to
package up and ship popular Javascript libraries for use in Clojurescript [1].

While these packages have only been compatible with a Boot-based
Clojurescript build chain we fully intend to make them work with this
new approach to handle external Javascript libraries.

That said a bunch of libraries are already in place, we tracked down
extern files etc, so the changes to make them compatible should be
rather small. Contributions are very welcome. I hope to get one of the
libs compatible tomorrow so the migration path for the others is
clear.

A general question/concern I'd like to voice in that context is that
this change makes it hard to split JS preamble from our compiled
Clojurescript.  Given that the Clojurescript code might change on a
daily basis and the preamble mostly stays the same this forces users
to re-download our (unchanged) JS dependencies over and over again,
while caching them would have provided us with much better performance.

David Nolen

unread,
Jan 24, 2015, 10:54:03 AM1/24/15
to clojur...@googlegroups.com, clojure
On Sat, Jan 24, 2015 at 10:43 AM, Martin Klepsch <martin...@googlemail.com> wrote:
A general question/concern I'd like to voice in that context is that
this change makes it hard to split JS preamble from our compiled
Clojurescript.  Given that the Clojurescript code might change on a
daily basis and the preamble mostly stays the same this forces users
to re-download our (unchanged) JS dependencies over and over again,
while caching them would have provided us with much better performance.

I understand the desire/need for splitting out foreign deps but this should be addressed separately with the coming Google Closure Module support

David

David Nolen

unread,
Jan 24, 2015, 11:09:08 AM1/24/15
to clojure, clojur...@googlegroups.com
On Sat, Jan 24, 2015 at 11:01 AM, Khalid Jebbari <khalid....@gmail.com> wrote:
A question (maybe stupid/obvious) : why do you need to declare the "min" version of js lib ? The normal version + the extern file is all that's needed to compress the file with the Closure Compiler, no ?

Declaring the minified version is completely optional.

JavaScript libraries generally ship minified versions so why not leverage it? Including the un-minified version into the Closure Compiler build is usually asking for trouble (I encountered problems with React in the past) and just slows down advanced builds by having more JavaScript to process, especially large libraries like React (>30KLOC).

David

Daniel Kersten

unread,
Jan 24, 2015, 11:31:47 AM1/24/15
to clojur...@googlegroups.com, clojure
Thanks David for your continued hard work - another fantastic release. This looks really good and solves real problems.

--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
To post to this group, send email to clojur...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojurescript.

Moritz Ulrich

unread,
Jan 24, 2015, 11:43:17 AM1/24/15
to David Nolen, clojure, clojur...@googlegroups.com
> --
> Note that posts from new members are moderated - please be patient with your first post.
> ---
> You received this message because you are subscribed to the Google Groups "ClojureScript" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
> To post to this group, send email to clojur...@googlegroups.com.
> Visit this group at http://groups.google.com/group/clojurescript.

As I already said on IRC: This is just *great*. I tried it in both
applications I'm working on right now and both saw a (meaningful)
reduction in size.

Thanks again for this great release!
signature.asc

David Nolen

unread,
Jan 24, 2015, 12:04:32 PM1/24/15
to clojure, clojur...@googlegroups.com
Some further explanation on packaging JavaScript libraries for ClojureScript consumption https://github.com/clojure/clojurescript/wiki/Foreign-Dependencies

David Nolen

unread,
Jan 24, 2015, 3:58:02 PM1/24/15
to clojure, clojur...@googlegroups.com
I just cut 0.0-2723. The significant change is an often requested feature - that script inclusion for :none be the same as other build settings. This is finally supported if you provide a :main entry specifying a namespace.

{:main hello-world.core
 :output-to "hello_world.js"
 :output-dir "out"
 :optimizations :none
 :source-map true}

Then your markup just needs:

<script src="hello_world.js" type="text/javascript"></script>

Same as :advanced.

David

On Sat, Jan 24, 2015 at 10:10 AM, David Nolen <dnolen...@gmail.com> wrote:

David Nolen

unread,
Jan 24, 2015, 4:22:16 PM1/24/15
to clojure, clojur...@googlegroups.com
And just cut 0.0-2725 to address a Node.js target support regression.

David

On Sat, Jan 24, 2015 at 10:10 AM, David Nolen <dnolen...@gmail.com> wrote:

David Nolen

unread,
Jan 24, 2015, 8:42:46 PM1/24/15
to clojure, clojur...@googlegroups.com
Just cut 0.0-2727 to fix an issue around the new :main support. ClojureScript now supports an :asset-path option to control how the :main script imports other scripts in order to respect whatever asset configuration you may have set up for your web server.

David

David Nolen

unread,
Jan 25, 2015, 2:42:43 PM1/25/15
to clojure, clojur...@googlegroups.com
I strongly recommend the Clojure(Script) community join forces when packaging libraries to avoid duplicated effort and dependency conflicts.

CLJSJS seems like a good initiative to me along these lines: http://cljsjs.github.io

David

On Sun, Jan 25, 2015 at 1:49 PM, Vladimir Bokov <bokov...@gmail.com> wrote:
Thanks David!

I also already packaged https://github.com/razum2um/jquery-cljs using your react repo as example

суббота, 24 января 2015 г., 21:10:29 UTC+6 пользователь David Nolen написал:

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com

Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

David Nolen

unread,
Jan 25, 2015, 5:49:00 PM1/25/15
to clojure, clojur...@googlegroups.com

On Sun, Jan 25, 2015 at 5:40 PM, Vladimir Bokov <bokov...@gmail.com> wrote:
I saw this initiative, but I hardly imagine a Github repo/organisation managing the whole infrastructure. As you said: we have clojars and maven.

For the most popular libraries having a curated set is going to be important - Maven knobs can only do so much.

Besides, they offer a naming convention and versioning policy, which I fully agree with, but I see no need to use 'boot-cljsjs' to follow them.

CLJSJS packaged JARs will not require boot-cljs - they will package JAR artifacts with the required deps.cljs.
 
After all, I think, your example of react packaging https://github.com/swannodette/react-cljs is clean and simple enough.

And it will be deprecated as soon as CLJSJS provides their own deps.cljs packaged version.

David

Ivan L

unread,
Jan 25, 2015, 7:28:01 PM1/25/15
to clo...@googlegroups.com, clojur...@googlegroups.com
To this end I would hope everyone takes a look at using webjars.org as their source for frontend libraries.  imo, it's a way forward to dep mgmt outside of bower/npm/node etc.

David Nolen

unread,
Jan 25, 2015, 8:11:22 PM1/25/15
to clojur...@googlegroups.com, clojure
Webjars doesn't package JS libs in a useful way for ClojureScript.
--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.

Laurent PETIT

unread,
Jan 26, 2015, 3:08:43 AM1/26/15
to clojur...@googlegroups.com, clojure
Yet ?
--
Laurent Petit

David Nolen

unread,
Jan 26, 2015, 8:15:01 AM1/26/15
to clojur...@googlegroups.com, clojure
On Mon, Jan 26, 2015 at 3:08 AM, Laurent PETIT <lauren...@gmail.com> wrote:
Yet ?

Right if Webjars was open to including the necessary information that would be great. I suspect this will be challenging since Webjars has chosen RequireJS as the runtime loading mechanism whereas deps.cljs provides Google Closure runtime dependency information for a specific compile to JavaScript language.

Likely the most promising way to integrate Webjars is to extract Google Closure information from the RequireJS file located in RequireJS supporting JARs. This processing could be performed by other tools like cljsbuild.

Still I have concerns about ClojureScript discoverability and Webjars - "is this Webjar fully ClojureScript compatible?".

David
Reply all
Reply to author
Forward
0 new messages