<< Back to previous view

[CLJS-101] Node fails with :whitespace, works with :simple Created: 17/Nov/11  Updated: 04/May/14  Resolved: 19/Apr/14

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Ulrik Sandberg Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: None
Environment:

NodeJS 0.6.1
MacOSX Lion



 Description   

Running the example from http://mmcgrana.github.com/2011/09/clojurescript-nodejs.html. Node fails if using :whitespace optimizations, but works when using :simple. The problem seems to be missing object literals.

The source is:

$ cat src/testing-cljs-node/hello.cljs 
(ns testing-cljs-node.hello)

(defn start [& _]
  (println "Hello World!"))

(set! *main-cli-fn* start)

Here is the compilation that results in the broken code:

$ cljsc src/testing-cljs-node/hello.cljs '{:optimizations :whitespace :pretty-print true :target :nodejs}' > out/hello.js
$ node out/hello.js

node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
TypeError: Cannot set property 'Unicode' of undefined
    at Object.<anonymous> (/Users/ulrik/Source/testing-cljs-node/out/hello.js:487:21)
    at Module._compile (module.js:432:26)
    at Object..js (module.js:450:10)
    at Module.load (module.js:351:31)
    at Function._load (module.js:310:12)
    at Array.0 (module.js:470:10)
    at EventEmitter._tickCallback (node.js:192:40)

When changing the optimizations to :simple, however, it works:

$ cljsc src/testing-cljs-node/hello.cljs '{:optimizations :simple :pretty-print true :target :nodejs}' > out/hello.js
$ node out/hello.js
The "sys" module is now called "util". It should have a similar interface.
Hello World!

The offending line 487 in the :whitespace code:

goog.provide("goog.string");
goog.provide("goog.string.Unicode");
goog.string.Unicode = {NBSP:"\u00a0"};  // line 487

In the working :simple code, the corresponding lines looks like this:

goog.string = {};
goog.string.Unicode = {NBSP:"\u00a0"};  // line 367

If I fix that by adding a line that creates the object, I get another error on line 901:

goog.provide("goog.userAgent.jscript");
goog.require("goog.string");
goog.userAgent.jscript.ASSUME_NO_JSCRIPT = false;  // line 901

In the working code, it looks like this:

goog.userAgent = {};
goog.userAgent.jscript = {};
goog.userAgent.jscript.ASSUME_NO_JSCRIPT = !1;  // line 683

And if I fix that, I get another error on line 975:

goog.provide("goog.debug.Error");
goog.debug.Error = function(opt_msg) {  // line 975

The working code:

goog.debug = {};
goog.debug.Error = function(a) {  // line 730

etc etc



 Comments   
Comment by David Nolen [ 19/Apr/12 11:49 PM ]

I'm not sure how to fix this since the implementation of goog.provide has no meaning in Node.js. As a compromise perhaps we should complain if you try to target node with :whitespace optimizations?

Comment by Ulrik Sandberg [ 20/Apr/12 5:50 AM ]

Sure, that would probably be sufficient to point the user to what the problem really is.

Perhaps it's obvious to everyone else that :whitespace doesn't make sense for Node, but it wasn't to me. But I can now deduce that :simple maybe resolves all provide/require into a single file, whereas :whitespace is an intermediate format that is not applicable for Node.

Comment by David Nolen [ 20/Apr/12 11:29 AM ]

I don't think it's obvious at all and many people have encountered this issue.

Comment by Tom Jack [ 16/Aug/12 2:54 AM ]

Something that seems to work is

#!/usr/bin/nodejs
var _cljsGlobal_ = {};
with (_cljsGlobal_) {
var COMPILED = false;
_cljsGlobal_.goog = {};
goog.global = _cljsGlobal_;
// ... rest of source
}

Does this cause any problems other than forcing e.g. ;module.exports = _cljsGlobal_.mori; in whitespace mode?

Comment by David Nolen [ 16/Aug/12 8:30 AM ]

Using with is probably not an acceptable solution.

Comment by David Nolen [ 28/Sep/12 10:08 PM ]

nclosure actually sounds kind of promising! Do we need to do anything more then recommend that people install nclosure if they want to work :whitespace or :none for the optimization level? If the nclosure approach is simple enough we could perhaps duplicate the strategy?

Comment by Paul deGrandis [ 29/Sep/12 5:27 PM ]

Haha I deleted the comment after I started digging into the code. I'll try to capture my adventure here.

CLJS's node compat layer is built in the opposite direction as nclojure - that is, we provide an externs file for node, and assume interop will happen that way.
nclosure's approach is to hijack the base interactions with goog, and decide to dispatch accordingly to Node or to the Closure lib, but it expects you're writing node-based code. Everything is pushed into shared object off of the node `globals`.

My hypothesis was: I can use the nclosure stuff, but allow ALL dispatching to go to closure (since we have externs file), which very well may call node stuff in the end. The small shim would allow for skirting around the issues described here.

I hacked together the js files and copied the result in to my CLJS-compiled-JS file. All looked good except - the nclosure JS code needs to be inserted into the file before compilation happens. The nclosure stuff redefines core goog interactions, which is why it works. Technically, we could do a preamble hook when we're gathering all the the files together (much like how main-cli-fn is at the tail of all files).

That said, when inserted into the right spot, it prevents the errors.

Comment by David Nolen [ 19/Apr/14 10:14 AM ]

fixed https://github.com/clojure/clojurescript/commit/0c7b31ada01237de33cef77b817ccef3f2b3576d

Comment by Quiz Dr [ 04/May/14 3:43 AM ]

FWIW glad I found this page; I am getting the exact same "type error" error on this line:

goog.string.Unicode = {NBSP:"\u00a0"};

When I use the "whitespace" optimization and run my compiled JS inside Qt 5.2 which now offers Javascript as a native language for building device GUIs (it's pretty neat, and using ClojureScript for this is even neater if I can get it to work).

The type error went away when I switched to "simple" optimization. I am using CS 2202 with cljsbuild 1.0.3.

Generated at Fri Nov 28 17:07:37 CST 2014 using JIRA 4.4#649-r158309.