coffee-script icon indicating copy to clipboard operation
coffee-script copied to clipboard

Compiled JS includes additional dependency (buggy without wrapper)

Open akidee opened this issue 12 years ago • 27 comments

Compiling iced CS to JS includes the problem that a new dependency is added: iced-coffee-script. CS' strength is that you can use the compiled JS out of the box, and iced should work in the same way: you can execute the compiled JS without any additional dependencies. Compiling a CS file with the -b flag will omit the wrapper, but the variable "iced" will be undefined.

akidee avatar Jan 29 '12 11:01 akidee

This should be solved by:

iced -I inline yourfile.coffee

Which will instruct the iced compiler/interpreter to inline a simplified runtime library. Is this what you need?

maxtaco avatar Jan 29 '12 14:01 maxtaco

arguably, it should do that without a command line parameter. especially since --help says

  -I, --runtime      how to include the iced runtime, one of #{runtime_modes_str}; default is "inline"

also if you do iced --runtime window, it should only declare iced once so that several files can use it. Right now it's the same thing as inline, except it uses window.iced instead of a local variable.

andrewrk avatar Jan 29 '12 22:01 andrewrk

Online docs bugs fixed in 62c9d13, thanks for the report. Here's my thinking behind this. If you're running the script from the command line, chances are you're firing up a node program. So everything still works without flags. If you're compiling for the browser, then you are already specifying flags, so what's the harm in one more.

As for the window.iced only being defined once, I agree, but if you're just compiling these to JS files that will be picked up by your webserver, then the compiler would have to guess in which combinations they show up on your website, which of course it can't know. Here are some other pointers: the runtime iced libs are also available in extras/coffee-script-iced.js. And also, you can compile some source files with -I none, which will leave the runtime out. You can of course combine this with -I window.

Still of course want to get this right. How would you recommend only setting window.iced once? Should there be a separate flag, -I first, which inclues window.iced on the first file, and nothing for the rest?

maxtaco avatar Jan 30 '12 00:01 maxtaco

$ cat test.coffee 
foo = (cb) -> cb()

await foo defer()

console.log "hi"
$ iced test.coffee 
Error: Cannot find module 'iced-coffee-script'
    at Function._resolveFilename (module.js:334:11)
    at Function._load (module.js:279:25)
    at Module.require (module.js:357:17)
    at require (module.js:368:17)
    at Object.<anonymous> (/home/andy/tmp/test.coffee:5:10)
    at Object.<anonymous> (/home/andy/tmp/test.coffee:57:4)
    at Module._compile (module.js:432:26)
    at Object.run (/usr/local/lib/node_modules/iced-coffee-script/lib/coffee-script/coffee-script.js:92:25)
    at /usr/local/lib/node_modules/iced-coffee-script/lib/coffee-script/command.js:141:29
    at /usr/local/lib/node_modules/iced-coffee-script/lib/coffee-script/command.js:116:18

It doesn't work in node by default for me.

andrewrk avatar Jan 30 '12 00:01 andrewrk

For the window.iced thing, if you're running the command like this: iced -c src/1.coffee src/2.coffee src/etc.coffee then it should put that window.iced thing above all the local scope things. For example, it should work like this:

$ cat test.coffee 
foo = (cb) -> cb()

await foo defer()

console.log "hi"
$ cat test2.coffee 
blah = (cb) -> cb()

await blah defer()

console.log "blah"

$ iced -p -c test*coffee
// Generated by IcedCoffeeScript 1.2.0i
window.iced = {
  Deferrals: (function() {

    function _Class(_arg) {
      this.continuation = _arg;
      this.count = 1;
      this.ret = null;
    }

    _Class.prototype._fulfill = function() {
      if (!--this.count) return this.continuation(this.ret);
    };

    _Class.prototype.defer = function(defer_params) {
      var _this = this;
      ++this.count;
      return function() {
        var inner_params, _ref;
        inner_params = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        if (defer_params != null) {
          if ((_ref = defer_params.assign_fn) != null) {
            _ref.apply(null, inner_params);
          }
        }
        return _this._fulfill();
      };
    };

    return _Class;

  })(),
  findDeferral: function() {
    return null;
  }
};
(function() {
  var blah, iced, __iced_deferrals, __iced_k,
    _this = this;

  __iced_k = function() {};

  blah = function(cb) {
    return cb();
  };

  (function(__iced_k) {
    __iced_deferrals = (new window.iced).Deferrals(__iced_k, {
      filename: 'test2.coffee'
    });
    blah(__iced_deferrals.defer({
      lineno: 2
    }));
    __iced_deferrals._fulfill();
  })(function() {
    return console.log("blah");
  });

}).call(this);
// Generated by IcedCoffeeScript 1.2.0i
(function() {
  var foo, iced, __iced_deferrals, __iced_k,
    _this = this;

  __iced_k = function() {};

  foo = function(cb) {
    return cb();
  };

  (function(__iced_k) {
    __iced_deferrals = (new window.iced).Deferrals(__iced_k, {
      filename: 'test.coffee'
    });
    foo(__iced_deferrals.defer({
      lineno: 2
    }));
    __iced_deferrals._fulfill();
  })(function() {
    return console.log("hi");
  });

}).call(this);

andrewrk avatar Jan 30 '12 00:01 andrewrk

This may be related but I'm trying to incorporate iced as an alternative coffeescript compiler in my sbt plugin by using the extras/coffee-script.js through Rhino programmatically.

The generated code is not valid because require is not defined.

iced = require('iced-coffee-script').iced;

Is the iced version of the coffee-script.js compiler not meant to be self-contained or multiple files need to be evaluated before compilation? It would be nice to have something self contained and environment-agnostic like the original extras/coffee-script.js impl.

softprops avatar Feb 04 '12 02:02 softprops

I agree this is a problem. I'm getting the same issue. Running node v.0.6.8, and iced 1.2.0l, both installed globally on a fairly modern Linux distro, and using your example:

iced -I node await.coffee yahoo.com
Error: Cannot find module 'iced-coffee-script'

It does work with the "inline" mode.

elfsternberg avatar Feb 08 '12 20:02 elfsternberg

Can someone point me to the code that executes conditionally when -I is passed the node script. I'd like to try and get it to work programmatically outside the context of a node execution env.

softprops avatar Feb 08 '12 20:02 softprops

Traveling for a few days, won't get to look at this until next week. In the mean time, see src/nodes.coffee and look for the class IcedRuntime. A single instance of this object is inserted into the AST if iced features are needed. And it looks in the options argument to see which runtime was asked for with the -I flag on the command line. Cheers!

maxtaco avatar Feb 09 '12 02:02 maxtaco

I'd like to suggest/request another flag that tells the compiler to spit out a copy of the window variant of the runtime into a specific file that can be loaded into the browser once and shared.

I've kinda faked this myself by compiling a do-nothing stub:

if false then return

with iced --runtime window -c path/to/stub.iced, but that has an extra block of do-nothing code in it that could be avoided otherwise.

ghost avatar Feb 13 '12 21:02 ghost

@khiltd that's an interesting suggestion, there used to be something like that....The reason I killed it is that the inlined runtime isn't as full-featured as the runtime that's in iced.coffee. Maybe I should fix that first....

maxtaco avatar Feb 15 '12 16:02 maxtaco

Really? Seems to be working great for me, but maybe I'm not exercising all its features just yet. I'll have to look at what all's missing.

I can obviously copy that file out of there and slap it right in my repo, but then it runs the risk of falling out of step with future releases that might get slapped on new boxes that have NPM invoked by deploy script. Pretty minor issue, but it'd be cool if there were an officially sanctioned way to spit all the innards out.

ghost avatar Feb 15 '12 17:02 ghost

@khiltd the inlined version doesn't have code for Rendezvous or the stack-overrun-protector (when you call await in a tight loop and never go back to the main event loop). I spent some time trying unsuccessfully to get the full version to inline today, maybe I'l revisit. I'm trying not to bloat the inlined code is the challenge....

@khiltd to your earlier point, now with 1.2.0o, you can issue the --runforce flag to force a runtime even if it's not needed. So you can do something like this:

 ./bin/coffee -I window --runforce --print /dev/null

And you'll get a runtime as you want.

@softprops did you work out your issues?

And @superjoe30, while I agree it would also work to have the inlined runtime outside the scope guard, is there an issue with having it inside? Seems like it should still work, let me know if not.

In other words, I'd like to close this issue if it's OK with everyone....

maxtaco avatar Feb 16 '12 23:02 maxtaco

Neat.

NPM's telling me it's installing 1.2.0o, but -v says it's 1.2.0n, and it definitely doesn't recognize the --runforce flag. Am I doing something dumb?

% sudo npm install -g iced-coffee-script                                                                                                     
npm http GET https://registry.npmjs.org/iced-coffee-script
npm http 304 https://registry.npmjs.org/iced-coffee-script
/usr/local/bin/iced -> /usr/local/lib/node_modules/iced-coffee-script/bin/coffee
/usr/local/bin/icake -> /usr/local/lib/node_modules/iced-coffee-script/bin/cake
[email protected] /usr/local/lib/node_modules/iced-coffee-script 
% iced -v                                                                                                                                    
IcedCoffeeScript version 1.2.0n
% /usr/local/lib/node_modules/iced-coffee-script/bin/coffee -v                            
IcedCoffeeScript version 1.2.0n

% iced -I window --runforce --print                                                 

node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
Error: unrecognized option: --runforce
    at OptionParser.parse (/usr/local/lib/node_modules/iced-coffee-script/lib/coffee-script/optparse.js:48:17)
    at /usr/local/lib/node_modules/iced-coffee-script/lib/coffee-script/command.js:409:29
    at Object.run (/usr/local/lib/node_modules/iced-coffee-script/lib/coffee-script/command.js:52:5)
    at Object.<anonymous> (/usr/local/lib/node_modules/iced-coffee-script/bin/coffee:7:41)
    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)


ghost avatar Feb 16 '12 23:02 ghost

Sorry, I messed up the npm publish it seems. Try to reinstall 1.2.0o, did that work?

maxtaco avatar Feb 17 '12 00:02 maxtaco

Nope; still pulling n it seems.

ghost avatar Feb 17 '12 00:02 ghost

WHAT THE! Ok, This time I verified it with npm's website, it's got to be right now. Really sorry about that!

maxtaco avatar Feb 17 '12 00:02 maxtaco

Still giving me n. Am I just getting a stale mirror or something? I don't really know how NPM works internally.

ghost avatar Feb 17 '12 00:02 ghost

Ah, it just thought it already had o so it wasn't bothering. Uninstall followed by install fixed it.

ghost avatar Feb 17 '12 00:02 ghost

Ok, great. I also brought a sledgehammer to bear on the problem and bumped to 1.2.0p. So long 1.2.0o, we hardly knew ye.

maxtaco avatar Feb 17 '12 00:02 maxtaco

Hi @maxtaco I haven't had a whole lot of time to look into it for my usecase. What I wanted was a sand-alone version akin the extras script you get with coffeescript that I could run through rhino. Ill have a go this weekend.

softprops avatar Feb 17 '12 16:02 softprops

@softprops that exists --- I've patched extras/coffee-script.js to have the iced runtime. As an example, the brochure site is using it, and it seems to work. Check out http://maxtaco.github.com/coffee-script, and see the include at the bottom of the page. All of that code is compiled with -I none, since the runtime is already available via that include.

maxtaco avatar Feb 18 '12 00:02 maxtaco

SWEET! I can't wait to try this out. Thanks @maxtaco for all your hard work.

softprops avatar Feb 18 '12 01:02 softprops

No problem, thanks for giving it a shot, let me know how it goes.

maxtaco avatar Feb 18 '12 01:02 maxtaco

Hi max.

I'm using the p version of your compiler and I still get dependent build artifacts in the javascript output of compilation.

With vanilla coffeescript, I can invoke the CoffeeScript compile function with the optional bare argument and the compiled javascript output has not dependencies on coffeescript itself.

With doing the same with iced coffeescript I get iced dependencies in the compiled javascript. When issuing a standard CoffeeScript.compile with no args, the outputted javascript contains a call to an undefined require method. I think this is because the default runtime assumption is node. When I provide the bare argument to compile I get an undefined error on a reference to iced on a line containing __iced_deferrals = new iced.Deferrals(__iced_k, {});.

Is there any way I could pass in an a flag to the compile method's arguments to tell the iced version of the compiler do to the inlining of the dependencies required for the outputted javascript?

softprops avatar Feb 19 '12 05:02 softprops

A better way of asking that might be, is there a way of not adding a runtime dependency in addition to compile time dependency?

softprops avatar Feb 19 '12 05:02 softprops

nm. I think I found my in here. I can call CoffeeScript.compile({bare: true, runtime:'inline'}). I think that's all I needed to know.

softprops avatar Feb 19 '12 05:02 softprops