Compiled JS includes additional dependency (buggy without wrapper)
Opened this issue · 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.
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?
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.
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?
$ 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.
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);
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.
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.
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.
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!
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.
@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....
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.
@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....
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
iced-coffee-script@1.2.0o /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)
Sorry, I messed up the npm publish it seems. Try to reinstall 1.2.0o, did that work?
Nope; still pulling n
it seems.
WHAT THE! Ok, This time I verified it with npm's website, it's got to be right now. Really sorry about that!
Still giving me n
. Am I just getting a stale mirror or something? I don't really know how NPM works internally.
Ah, it just thought it already had o
so it wasn't bothering. Uninstall followed by install fixed it.
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.
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 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.
No problem, thanks for giving it a shot, let me know how it goes.
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?
A better way of asking that might be, is there a way of not adding a runtime dependency in addition to compile time dependency?