Permalink
Browse files

Add command-line compiler hooks. To invoke, pass a file after -r and …

…listen for any of these events: 'compile', 'success' and 'exception'. Example:

    coffee -e -r ./snarl 'Hello!'

Contents of 'snarl.coffee' in the working directory:

    http = require 'http'
    CoffeeScript.on 'exception', (err) ->
      client  = http.createClient 9889, 'localhost'
      request = client.request 'GET', '/?d={"action":1,"applicationName":"CoffeeScript","title":' + JSON.stringify(err.message) + ',"description":' + JSON.stringify(err.stack) + ',"priority":3}'
      request.end()
      err.handled = yes

To examine arguments available for each event (for debugging and getting started), use `puts JSON.stringify arguments`.

See http://nodejs.org/api.html#modules-309 and NODE_PATH for more details on how -r looks for files.
  • Loading branch information...
StanAngeloff committed Aug 7, 2010
1 parent 0ada5a7 commit b1b78dca47c83986c9654ec51fd9993f90a795e5
Showing with 122 additions and 57 deletions.
  1. +73 −29 lib/command.js
  2. +7 −4 lib/optparse.js
  3. +35 −20 src/command.coffee
  4. +7 −4 src/optparse.coffee
View
@@ -1,14 +1,26 @@
(function() { (function() {
var BANNER, CoffeeScript, SWITCHES, _a, compileOptions, compileScript, compileScripts, compileStdio, exec, fs, lint, optionParser, options, optparse, parseOptions, path, printTokens, sources, spawn, usage, version, watch, writeJs; var BANNER, CoffeeScript, EventEmitter, SWITCHES, _a, _b, _c, compileOptions, compileScript, compileScripts, compileStdio, exec, fs, helpers, lint, optionParser, options, optparse, parseOptions, path, printTokens, sources, spawn, usage, version, watch, writeJs;
var __hasProp = Object.prototype.hasOwnProperty;
fs = require('fs'); fs = require('fs');
path = require('path'); path = require('path');
optparse = require('./optparse'); optparse = require('./optparse');
CoffeeScript = require('./coffee-script'); CoffeeScript = require('./coffee-script');
_a = require('child_process'); _a = require('./helpers');
spawn = _a.spawn; helpers = _a.helpers;
exec = _a.exec; _b = require('child_process');
spawn = _b.spawn;
exec = _b.exec;
_c = require('events');
EventEmitter = _c.EventEmitter;
helpers.extend(CoffeeScript, new EventEmitter());
BANNER = 'coffee compiles CoffeeScript source files into JavaScript.\n\nUsage:\n coffee path/to/script.coffee'; BANNER = 'coffee compiles CoffeeScript source files into JavaScript.\n\nUsage:\n coffee path/to/script.coffee';
SWITCHES = [['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JSLint'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-e', '--eval', 'compile a string from the command line'], ['--no-wrap', 'compile without the top-level function wrapper'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-n', '--nodes', 'print the parse tree that Jison produces'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']]; SWITCHES = [
['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JSLint'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-e', '--eval', 'compile a string from the command line'], [
'-r', '--require [FILE]', 'require the library, before executing your script', {
isList: true
}
], ['--no-wrap', 'compile without the top-level function wrapper'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-n', '--nodes', 'print the parse tree that Jison produces'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']
];
options = {}; options = {};
sources = []; sources = [];
optionParser = null; optionParser = null;
@@ -47,13 +59,13 @@
return compileScripts(); return compileScripts();
}; };
compileScripts = function() { compileScripts = function() {
var _b, _c, _d, _e; var _d, _e, _f, _g;
_b = []; _d = sources; _d = []; _f = sources;
for (_c = 0, _e = _d.length; _c < _e; _c++) { for (_e = 0, _g = _f.length; _e < _g; _e++) {
(function() { (function() {
var base, compile; var base, compile;
var source = _d[_c]; var source = _f[_e];
return _b.push((function() { return _d.push((function() {
base = source; base = source;
compile = function(source, topLevel) { compile = function(source, topLevel) {
return path.exists(source, function(exists) { return path.exists(source, function(exists) {
@@ -63,13 +75,13 @@
return fs.stat(source, function(err, stats) { return fs.stat(source, function(err, stats) {
if (stats.isDirectory()) { if (stats.isDirectory()) {
return fs.readdir(source, function(err, files) { return fs.readdir(source, function(err, files) {
var _f, _g, _h, _i, file; var _h, _i, _j, _k, file;
_f = []; _h = files; _h = []; _j = files;
for (_g = 0, _i = _h.length; _g < _i; _g++) { for (_i = 0, _k = _j.length; _i < _k; _i++) {
file = _h[_g]; file = _j[_i];
_f.push(compile(path.join(source, file))); _h.push(compile(path.join(source, file)));
} }
return _f; return _h;
}); });
} else if (topLevel || path.extname(source) === '.coffee') { } else if (topLevel || path.extname(source) === '.coffee') {
fs.readFile(source, function(err, code) { fs.readFile(source, function(err, code) {
@@ -86,13 +98,41 @@
})()); })());
})(); })();
} }
return _b; return _d;
}; };
compileScript = function(source, code, base) { compileScript = function(source, code, base) {
var codeOpts, js, o; var _d, _e, _f, _g, _h, _i, codeOpts, file, globalize, js, name, o, ref;
o = options; o = options;
codeOpts = compileOptions(source); codeOpts = compileOptions(source);
if (o.require) {
globalize = {
CoffeeScript: CoffeeScript
};
_d = globalize;
for (name in _d) {
if (!__hasProp.call(_d, name)) continue;
ref = _d[name];
(global[name] = ref);
}
_f = o.require;
for (_e = 0, _g = _f.length; _e < _g; _e++) {
file = _f[_e];
require(file.replace(/^(\.+\/)/, ("" + (process.cwd()) + "/$1")));
}
_i = globalize;
for (name in _i) {
if (!__hasProp.call(_i, name)) continue;
_h = _i[name];
delete global[name];
}
}
try { try {
CoffeeScript.emit('compile', {
source: source,
code: code,
base: base,
options: options
});
if (o.tokens) { if (o.tokens) {
return printTokens(CoffeeScript.tokens(code)); return printTokens(CoffeeScript.tokens(code));
} else if (o.nodes) { } else if (o.nodes) {
@@ -101,6 +141,7 @@
return CoffeeScript.run(code, codeOpts); return CoffeeScript.run(code, codeOpts);
} else { } else {
js = CoffeeScript.compile(code, codeOpts); js = CoffeeScript.compile(code, codeOpts);
CoffeeScript.emit('success', js);
if (o.print) { if (o.print) {
return print(js); return print(js);
} else if (o.compile) { } else if (o.compile) {
@@ -110,10 +151,13 @@
} }
} }
} catch (err) { } catch (err) {
if (!(o.watch)) { CoffeeScript.emit('exception', err);
if (!(o.watch || err.handled)) {
error(err.stack) && process.exit(1); error(err.stack) && process.exit(1);
} }
return puts(err.message); if (!(err.handled)) {
return puts(err.message);
}
} }
}; };
compileStdio = function() { compileStdio = function() {
@@ -175,19 +219,19 @@
return jsl.stdin.end(); return jsl.stdin.end();
}; };
printTokens = function(tokens) { printTokens = function(tokens) {
var _b, _c, _d, _e, _f, strings, tag, token, value; var _d, _e, _f, _g, _h, strings, tag, token, value;
strings = (function() { strings = (function() {
_b = []; _d = tokens; _d = []; _f = tokens;
for (_c = 0, _e = _d.length; _c < _e; _c++) { for (_e = 0, _g = _f.length; _e < _g; _e++) {
token = _d[_c]; token = _f[_e];
_b.push((function() { _d.push((function() {
_f = [token[0], token[1].toString().replace(/\n/, '\\n')]; _h = [token[0], token[1].toString().replace(/\n/, '\\n')];
tag = _f[0]; tag = _h[0];
value = _f[1]; value = _h[1];
return "[" + (tag) + " " + (value) + "]"; return "[" + (tag) + " " + (value) + "]";
})()); })());
} }
return _b; return _d;
})(); })();
return puts(strings.join(' ')); return puts(strings.join(' '));
}; };
View
@@ -7,7 +7,7 @@
return this; return this;
}; };
OptionParser.prototype.parse = function(args) { OptionParser.prototype.parse = function(args) {
var _a, _b, _c, _d, _e, arg, i, isOption, matchedRule, options, rule; var _a, _b, _c, _d, _e, arg, i, isOption, matchedRule, options, rule, value;
options = { options = {
arguments: [] arguments: []
}; };
@@ -21,7 +21,8 @@
for (_c = 0, _e = _d.length; _c < _e; _c++) { for (_c = 0, _e = _d.length; _c < _e; _c++) {
rule = _d[_c]; rule = _d[_c];
if (rule.shortFlag === arg || rule.longFlag === arg) { if (rule.shortFlag === arg || rule.longFlag === arg) {
options[rule.name] = rule.hasArgument ? args[i += 1] : true; value = rule.hasArgument ? args[i += 1] : true;
options[rule.name] = rule.isList ? (options[rule.name] || []).concat(value) : value;
matchedRule = true; matchedRule = true;
break; break;
} }
@@ -78,16 +79,18 @@
} }
return _a; return _a;
}; };
buildRule = function(shortFlag, longFlag, description) { buildRule = function(shortFlag, longFlag, description, options) {
var match; var match;
match = longFlag.match(OPTIONAL); match = longFlag.match(OPTIONAL);
longFlag = longFlag.match(LONG_FLAG)[1]; longFlag = longFlag.match(LONG_FLAG)[1];
options = options || {};
return { return {
name: longFlag.substr(2), name: longFlag.substr(2),
shortFlag: shortFlag, shortFlag: shortFlag,
longFlag: longFlag, longFlag: longFlag,
description: description, description: description,
hasArgument: !!(match && match[1]) hasArgument: !!(match && match[1]),
isList: !!options.isList
}; };
}; };
normalizeArguments = function(args) { normalizeArguments = function(args) {
View
@@ -5,11 +5,15 @@
# interactive REPL. # interactive REPL.
# External dependencies. # External dependencies.
fs = require 'fs' fs = require 'fs'
path = require 'path' path = require 'path'
optparse = require './optparse' optparse = require './optparse'
CoffeeScript = require './coffee-script' CoffeeScript = require './coffee-script'
{spawn, exec} = require 'child_process' {helpers} = require './helpers'
{spawn, exec} = require 'child_process'
{EventEmitter} = require 'events'
helpers.extend CoffeeScript, new EventEmitter
# The help banner that is printed when `coffee` is called without arguments. # The help banner that is printed when `coffee` is called without arguments.
BANNER = ''' BANNER = '''
@@ -21,19 +25,20 @@ BANNER = '''
# The list of all the valid option flags that `coffee` knows how to handle. # The list of all the valid option flags that `coffee` knows how to handle.
SWITCHES = [ SWITCHES = [
['-c', '--compile', 'compile to JavaScript and save as .js files'] ['-c', '--compile', 'compile to JavaScript and save as .js files']
['-i', '--interactive', 'run an interactive CoffeeScript REPL'] ['-i', '--interactive', 'run an interactive CoffeeScript REPL']
['-o', '--output [DIR]', 'set the directory for compiled JavaScript'] ['-o', '--output [DIR]', 'set the directory for compiled JavaScript']
['-w', '--watch', 'watch scripts for changes, and recompile'] ['-w', '--watch', 'watch scripts for changes, and recompile']
['-p', '--print', 'print the compiled JavaScript to stdout'] ['-p', '--print', 'print the compiled JavaScript to stdout']
['-l', '--lint', 'pipe the compiled JavaScript through JSLint'] ['-l', '--lint', 'pipe the compiled JavaScript through JSLint']
['-s', '--stdio', 'listen for and compile scripts over stdio'] ['-s', '--stdio', 'listen for and compile scripts over stdio']
['-e', '--eval', 'compile a string from the command line'] ['-e', '--eval', 'compile a string from the command line']
[ '--no-wrap', 'compile without the top-level function wrapper'] ['-r', '--require [FILE]', 'require the library, before executing your script', isList: yes]
['-t', '--tokens', 'print the tokens that the lexer produces'] [ '--no-wrap', 'compile without the top-level function wrapper']
['-n', '--nodes', 'print the parse tree that Jison produces'] ['-t', '--tokens', 'print the tokens that the lexer produces']
['-v', '--version', 'display CoffeeScript version'] ['-n', '--nodes', 'print the parse tree that Jison produces']
['-h', '--help', 'display this help message'] ['-v', '--version', 'display CoffeeScript version']
['-h', '--help', 'display this help message']
] ]
# Top-level objects shared by all the functions. # Top-level objects shared by all the functions.
@@ -88,18 +93,28 @@ compileScripts = ->
compileScript = (source, code, base) -> compileScript = (source, code, base) ->
o = options o = options
codeOpts = compileOptions source codeOpts = compileOptions source
if o.require
globalize = {CoffeeScript}
(global[name] = ref) for name, ref of globalize
require file.replace /^(\.+\/)/, "#{ process.cwd() }/$1" for file in o.require
delete global[name] for name of globalize
try try
CoffeeScript.emit 'compile', {source, code, base, options}
if o.tokens then printTokens CoffeeScript.tokens code if o.tokens then printTokens CoffeeScript.tokens code
else if o.nodes then puts CoffeeScript.nodes(code).toString() else if o.nodes then puts CoffeeScript.nodes(code).toString()
else if o.run then CoffeeScript.run code, codeOpts else if o.run then CoffeeScript.run code, codeOpts
else else
js = CoffeeScript.compile code, codeOpts js = CoffeeScript.compile code, codeOpts
CoffeeScript.emit 'success', js
if o.print then print js if o.print then print js
else if o.compile then writeJs source, js, base else if o.compile then writeJs source, js, base
else if o.lint then lint js else if o.lint then lint js
catch err catch err
error(err.stack) and process.exit 1 unless o.watch # Avoid using 'error' as it is a special event -- if there is no handler,
puts err.message # node will print a stack trace and exit the program.
CoffeeScript.emit 'exception', err
error(err.stack) and process.exit 1 unless o.watch or err.handled
puts err.message unless err.handled
# Attach the appropriate listeners to compile scripts incoming over **stdin**, # Attach the appropriate listeners to compile scripts incoming over **stdin**,
# and write them back to **stdout**. # and write them back to **stdout**.
View
@@ -30,7 +30,8 @@ exports.OptionParser = class OptionParser
matchedRule = no matchedRule = no
for rule in @rules for rule in @rules
if rule.shortFlag is arg or rule.longFlag is arg if rule.shortFlag is arg or rule.longFlag is arg
options[rule.name] = if rule.hasArgument then args[i += 1] else true value = if rule.hasArgument then args[i += 1] else true
options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value
matchedRule = yes matchedRule = yes
break break
throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule
@@ -69,15 +70,17 @@ buildRules = (rules) ->
# Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the # Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the
# description of what the option does. # description of what the option does.
buildRule = (shortFlag, longFlag, description) -> buildRule = (shortFlag, longFlag, description, options) ->
match = longFlag.match(OPTIONAL) match = longFlag.match(OPTIONAL)
longFlag = longFlag.match(LONG_FLAG)[1] longFlag = longFlag.match(LONG_FLAG)[1]
options or= {}
{ {
name: longFlag.substr 2 name: longFlag.substr 2
shortFlag: shortFlag shortFlag: shortFlag
longFlag: longFlag longFlag: longFlag
description: description description: description
hasArgument: !!(match and match[1]) hasArgument: !!(match and match[1])
isList: !!options.isList
} }
# Normalize arguments by expanding merged flags into multiple flags. This allows # Normalize arguments by expanding merged flags into multiple flags. This allows

0 comments on commit b1b78dc

Please sign in to comment.