ANN: ClojureScript 0.0-2060

496 views
Skip to first unread message

David Nolen

unread,
Nov 21, 2013, 10:12:55 AM11/21/13
to clojure, clojur...@googlegroups.com
ClojureScript, the Clojure compiler that emits JavaScript source code.


New release version: 0.0-2060

Leiningen dependency information:

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

Source map accuracy is considerably improved in this release. Source
maps now work great under incremental compilation regardless of the
level of optimization. PersistentVector performance is considerably
improved for conj and instantiation.

Enhancements:
* CLJS-683: :source-map-path compiler option to simplify web server
  integration
* enable-console-print! for console.log based printing
* *print-length* now supported

Bug fixes:
* CLJS-691: IComparable for keywords and symbols
* CLJS-674: relativization of source map paths
* CLJS-687: error when deftype/record used as a function
* CLJS-639: warnings when record initialized incorrectly
* CLJS-672: source maps + optimizations + :libs breaks building
* CLJS-676: source map stale under incremental compilation + closure
  optimization
* CLJS-684: throw on circular dependency
* CLJS-583: duplicate keys in sets
* CLJS-680: function name shadows JS globals
* CLJS-699: namespaced keyword regression
* CLJS-647: js-obj keys could not be expressions

Thomas Heller

unread,
Nov 21, 2013, 12:57:46 PM11/21/13
to clo...@googlegroups.com, clojur...@googlegroups.com
Amazing work! Thanks to everyone involved.

Are there any hopes of optimizing things in the source-map department? It adds about 30sec (on optimize only, compile times are fine) to my build which makes it unsuitable for development.

A while back I opened http://dev.clojure.org/jira/browse/CLJS-474, it adds no compile-time and it helps a lot in finding out WHERE stuff happened. Can't compare it to full source maps but the stack traces no longer leave you completely helpless. The patch no longer works since some stuff in that area has changed, but should be fairly simple to get it working again. I'd be happy too if there is any interest.

See my initial description https://groups.google.com/d/msg/clojurescript/ZX6M4KXx8I8/0-NSjci9EEUJ about what it does.

To recap, it turns the standard cljs stack traces from


Uncaught Error: No protocol method IElement.-to-dom defined for type null:
missing_protocol
(anonymous function)
_to_dom
append__2
append
append__1
append
popup_open__delegate
popup_open
list_objects
dom_event_handler
goog.events.Listener.handleEvent
goog.events.fireListener
goog.events.handleBrowserEvent_
(anonymous function)


into


Uncaught Error: No protocol method IElement.-to-dom defined for type null:
fn_cljs_DOT_core_SLASH_missing_protocol_78
(anonymous function)
fn_thheller_DOT_web_DOT_dom_SLASH__to_dom_89
fn_thheller_DOT_web_DOT_dom_SLASH_append_122__2
fn_thheller_DOT_web_DOT_dom_SLASH_append_122
fn_thheller_DOT_web_DOT_dom_SLASH_append_122__1
fn_thheller_DOT_web_DOT_dom_SLASH_append_122
fn_thheller_DOT_debug_SLASH_popup_open_108__delegate
fn_thheller_DOT_debug_SLASH_popup_open_108
fn_thheller_DOT_debug_SLASH_list_objects_121
fn_thheller_DOT_object_SLASH_dom_event_handler_169
goog.events.Listener.handleEvent
goog.events.fireListener
goog.events.handleBrowserEvent_
(anonymous function)

All cljs functions are named with a fn_<ns>/<name>_<source line> pattern, since at least Chrome is not smart enough to figure out the function names on its own. So fn_thheller_DOT_web_DOT_dom_SLASH_append_122 is thheller.web.dom/append in line 122, its only a minor debug helper but it saved me tons of time.


Regards,
/thomas

David Nolen

unread,
Nov 21, 2013, 1:11:26 PM11/21/13
to clojur...@googlegroups.com
On Thu, Nov 21, 2013 at 12:57 PM, Thomas Heller <th.h...@gmail.com> wrote:
Amazing work! Thanks to everyone involved.

Are there any hopes of optimizing things in the source-map department? It adds about 30sec (on optimize only, compile times are fine) to my build which makes it unsuitable for development.

During development why are you enabling optimizations at all?

David 

Thomas Heller

unread,
Nov 21, 2013, 3:33:59 PM11/21/13
to clojur...@googlegroups.com
Various reasons, technically I could do without but I want my development environment to be as close to production as I can make it. Which currently means dev = optimizations whitespace, prod = advanced, everything else identical (html/js wise).

I built a framework which makes some assumptions, I'll try to explain them based on the project I'm currently developing. (http://www.smartchecker.de, its german, but the relevant source bits aren't ;)

1) Source is split into modules, modules group common functionality. Pages depend on modules.

http://www.smartchecker.de/ depends on common
http://www.smartchecker.de/tarifvergleich/smartphone depends on common+calc

This is an optimization since I want to keep sources small and not everyone needs to download the whole thing (which clocks in over 1mb js, before gzip), most pages do just fine with common. When a module is finished loading it looks for "init functions" in the source.

2) init functions run when the MODULE finished loading NOT on document ready/load. This again is an optimization, but averages out at about 50-250ms faster code execution (depends on connection and stuff). Init functions also carry a reference dom node (usually self or parent) so a script has an easy way to reference a location in the document without making up an ID and referencing it at some other point.

They look something like this

<script data-fn="smartchecker.calc.ui_init" data-module="calc" data-ref="parent" type="shadow/run">(edn string which represent args)</script>

This will be run when the module calc has finished loading and will call smartchecker.calc.ui_init(script.parentNode, some more args). This has the benefit of "calling" a javascript function WITHOUT any javascript having been loaded prior. (Relevant code: https://github.com/thheller/shadow/blob/master/src/shadow/api.cljs#L52)

If you look at the source of the site you'll see that every bit a javascript is included at the end of the body. Typically you load jquery in the head which blocks another couple ms. Angular uses a similar approach of marking the HTML which I don't really like. But its still way better than $(function() {});

Long story short, I want/need module boundaries. There is a bunch more stuff the framework does which I need to finalize/document at some point, but its not relevant here anyways.

So far I have used optimizations whitespace in dev because it carries basically no overhead, "looks" like production (eg. one file per module). So there really was no reason not to. The performance aspect is not really relevant during development, but the "doesnt-look-completely-different" aspect does.


Hope that wasn't way too confusing. I thought about the whole "thing" a lot in the past year and so far I'm very happy with it. Except for the missing source-map goodness. :P

Maybe I'll re-think my dev requirements, they are kind of strict. :P

Cheers,
/thomas


PS: all this will be obsolete with HTTP2, but who knows how long thats gonna take. I'd settle for a sane cross-browser <script defer> right now ...

David Nolen

unread,
Nov 21, 2013, 3:42:44 PM11/21/13
to clojur...@googlegroups.com
Hmm, I'm not sure there's much we can do for you if you feel compelled to use optimizations. You're going to pay for the generation of the Closure source map and the necessary merge with CLJS source map information.

There's nothing slow about source map generation as evidenced by using :optimizations :none which avoid the Closure generation and merge step. If you have a bright idea about how we can make your particular use case faster - please let us know, and of course a patch is most welcome!

David


--
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.

Chas Emerick

unread,
Nov 21, 2013, 5:49:38 PM11/21/13
to clojur...@googlegroups.com
Why can't you can have it both ways? Use :none when doing intensive
development, but use :advanced for automated unit, specification, and
integration testing. This sort of environment/context parameterization
is readily achieved via a couple of Leiningen profiles.

(This will get even easier once cljsbuild allows you to set default
compiler options that will apply to all builds that don't override them,
thus eliminating a lot of the repetition that is currently necessary to
define different :cljsbuild options on a per-profile basis.)

- Chas

Thomas Heller

unread,
Nov 21, 2013, 6:50:38 PM11/21/13
to clojur...@googlegroups.com
Chas I think you missed my point. I'm using separate profiles for my dev/prod builds but I'm not using cljsbuild since it does not support JSModules.

I guess I'll have to live with my choices for now, :none currently is not an option.

Feng Hou

unread,
Nov 21, 2013, 10:43:02 PM11/21/13
to clojur...@googlegroups.com, clojure
On Thursday, November 21, 2013 10:12:55 AM UTC-5, David Nolen wrote:
> ClojureScript, the Clojure compiler that emits JavaScript source code.
>
>
> README and source code: https://github.com/clojure/clojurescript
>
>
>
> New release version: 0.0-2060
>
>
> Leiningen dependency information:
>
>
>     [org.clojure/clojurescript "0.0-2060"]
>
>
> Source map accuracy is considerably improved in this release. Source
>
> maps now work great under incremental compilation regardless of the
> level of optimization. PersistentVector performance is considerably
> improved for conj and instantiation.
>
>
>
> Enhancements:
> * CLJS-683: :source-map-path compiler option to simplify web server
>   integration
> * enable-console-print! for console.log based printing
> * *print-length* now supported
>
>
>
> Bug fixes:
> * CLJS-691: IComparable for keywords and symbols
> * CLJS-674: relativization of source map paths

I'm stuck with java 6 on an old MacAir. This patch does the same thing without using java 7 methods. All asserts in these files pass on Mac and Linux.

diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj
index b13adf0..e67409c 100644
--- a/src/clj/cljs/closure.clj
+++ b/src/clj/cljs/closure.clj
@@ -987,8 +987,8 @@

(defn same-or-subdirectory-of? [dir path]
"Checks that path names a file or directory that is the dir or a subdirectory there of."
- (let [dir-path (.toAbsolutePath (.toPath (io/file dir)))
- path-path (.toAbsolutePath (.toPath (io/file path)))]
+ (let [dir-path (.getCanonicalPath (io/file dir))
+ path-path (.getCanonicalPath (io/file path))]
(.startsWith path-path dir-path)))

(defn check-output-to [{:keys [output-to] :as opts}]
diff --git a/src/clj/cljs/source_map.clj b/src/clj/cljs/source_map.clj
index 9363601..edc5739 100644
--- a/src/clj/cljs/source_map.clj
+++ b/src/clj/cljs/source_map.clj
@@ -165,14 +165,12 @@
:default
(let [unrelativized-jpath (-> bare-munged-path
io/file
- .toPath
- .toAbsolutePath)
+ .toURI)
source-map-parent-jpath (-> source-map
io/file
- .toPath
- .toAbsolutePath
- .getParent)]
- (str (.relativize source-map-parent-jpath unrelativized-jpath))))))
+ .getParentFile
+ .toURI)]
+ (.getPath (.relativize source-map-parent-jpath unrelativized-jpath))))))

(defn encode
"Take an internal source map representation represented as nested

Tim Visher

unread,
Nov 21, 2013, 10:48:42 PM11/21/13
to clojur...@googlegroups.com, clojure
Thanks for the patch, Feng.

Patches don't tend to get discussed on the mailing list for Clojure.
Do you have your CA signed yet? http://clojure.org/contributing

Ironically, I had basically produced this patch, line for line, a
couple of hours ago. Hopefully we can get a release out soon. This was
totally my fault. :\

For now, you should be able to `lein install` your patched version and
depend on that until the official release goes out. Sorry!

Feng Hou

unread,
Nov 21, 2013, 11:15:30 PM11/21/13
to clojur...@googlegroups.com, clojure
On Thursday, November 21, 2013 10:48:42 PM UTC-5, Tim Visher wrote:
> Thanks for the patch, Feng.
>
>
>
> Patches don't tend to get discussed on the mailing list for Clojure.
>
> Do you have your CA signed yet? http://clojure.org/contributing
>
>
>
> Ironically, I had basically produced this patch, line for line, a
>
> couple of hours ago. Hopefully we can get a release out soon. This was
>
> totally my fault. :\
>
>
>
> For now, you should be able to `lein install` your patched version and
>
> depend on that until the official release goes out. Sorry!
>


Tim - Not a problem at all. It's a travel fix. Appreciate if you could submit your patch.

Regards,
Feng

Feng Hou

unread,
Nov 21, 2013, 11:43:06 PM11/21/13
to clojur...@googlegroups.com, clojure
On Thursday, November 21, 2013 10:12:55 AM UTC-5, David Nolen wrote:
Hi David,

Got these errors in optimizations :none with source-map

Uncaught TypeError: undefined is not a function core.cljs:3852
Uncaught TypeError: Cannot read property 'EMPTY' of undefined net.cljs:24
Uncaught TypeError: Cannot call method 'call' of undefined repl.cljs:21
Uncaught TypeError: undefined is not a function template.cljs:7
Uncaught TypeError: Cannot read property 'EMPTY' of undefined

The first one above points to this line at PersistentQueue.

(set! cljs.core.PersistentQueue.EMPTY (PersistentQueue. nil 0 nil [] 0))

In simple mode without source-map, points to this line

cljs.core.PersistentQueue.EMPTY = new cljs.core.PersistentQueue(null, 0, null, cljs.core.with_meta(cljs.core.PersistentVector.EMPTY, new cljs.core.PersistentArrayMap(null, 2, [new cljs.core.Keyword(null, "end-line", "end-line", 2693041432), 3852, new cljs.core.Keyword(null, "end-column", "end-column", 3799845882), 69], null)), 0);

No error in advanced mode though (optimized out?)

Thanks,
Feng

David Nolen

unread,
Nov 22, 2013, 5:36:43 AM11/22/13
to clojur...@googlegroups.com, clojure
Looks like we're not eliding some new metadata introduced by tools.reader.

Xu Hui Hui

unread,
Nov 27, 2013, 7:26:19 AM11/27/13
to clojur...@googlegroups.com, clojure
Got the same error regarding cljs.core.PersistentArrayMap (undefined is not a function) when using :simple cljsbuild. Upgrading clojurescript from 0.0-2060 to 0.0-2080 fixed the problem for me.

在 2013年11月22日星期五UTC+8下午12时43分06秒,Feng Hou写道:

Xu Hui Hui

unread,
Nov 27, 2013, 7:27:23 AM11/27/13
to clojur...@googlegroups.com, clojure
Got the same error regarding cljs.core.PersistentArrayMap (undefined is not a function) when using :simple cljsbuild. Upgrading clojurescript from 0.0-2060 to 0.0-2080 fixed the problem for me.

在 2013年11月22日星期五UTC+8下午12时43分06秒,Feng Hou写道:
Reply all
Reply to author
Forward
0 new messages