merged in Trevor Burnham's recursive coffee compilation, with some adjustments...

This commit is contained in:
Jeremy Ashkenas 2010-05-04 23:22:28 -04:00
parent c051daee2f
commit d64b8fd9d8
2 changed files with 84 additions and 104 deletions

View File

@ -1,5 +1,5 @@
(function(){ (function(){
var BANNER, CoffeeScript, SWITCHES, _a, base, compile_options, compile_script, compile_scripts, compile_stdio, exec, fs, is_watched, lint, option_parser, options, optparse, parse_options, path, print_tokens, sources, spawn, usage, version, watch, write_js; var BANNER, CoffeeScript, SWITCHES, _a, compile_options, compile_script, compile_scripts, compile_stdio, exec, fs, lint, option_parser, options, optparse, parse_options, path, print_tokens, sources, spawn, usage, version, watch, write_js;
// The `coffee` utility. Handles command-line compilation of CoffeeScript // The `coffee` utility. Handles command-line compilation of CoffeeScript
// into various forms: saved into `.js` files or printed to stdout, piped to // into various forms: saved into `.js` files or printed to stdout, piped to
// [JSLint](http://javascriptlint.com/) or recompiled every time the source is // [JSLint](http://javascriptlint.com/) or recompiled every time the source is
@ -16,12 +16,10 @@
// 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 = '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';
// 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 = [['-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'], ['-m', '--monitor', 'show a message every time a script is compiled'], ['-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'], ['--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']];
// Top-level objects shared by all the functions. // Top-level objects shared by all the functions.
options = {}; options = {};
sources = []; sources = [];
base = '';
is_watched = {};
option_parser = null; option_parser = null;
// Run `coffee` by parsing passed options and determining what action to take. // Run `coffee` by parsing passed options and determining what action to take.
// Many flags cause us to divert before compiling anything. Flags passed after // Many flags cause us to divert before compiling anything. Flags passed after
@ -60,62 +58,48 @@
// compile them. If a directory is passed, recursively compile all source // compile them. If a directory is passed, recursively compile all source
// files in it and all subdirectories. // files in it and all subdirectories.
compile_scripts = function compile_scripts() { compile_scripts = function compile_scripts() {
var compile, run; var _b, _c, _d, _e, base, compile, source;
compile = function compile(source) { _b = []; _d = sources;
if (is_watched[source]) { for (_c = 0, _e = _d.length; _c < _e; _c++) {
return null; source = _d[_c];
} _b.push((function() {
return path.exists(source, function(exists) { base = source;
if (!(exists)) { compile = function compile(source) {
throw new Error(("File not found: " + source)); return path.exists(source, function(exists) {
} if (!(exists)) {
return fs.stat(source, function(err, stats) { throw new Error(("File not found: " + source));
if (stats.isDirectory()) { }
return fs.readdir(source, function(err, files) { return fs.stat(source, function(err, stats) {
var _b, _c, _d, _e, file; if (stats.isDirectory()) {
_b = []; _d = files; return fs.readdir(source, function(err, files) {
for (_c = 0, _e = _d.length; _c < _e; _c++) { var _f, _g, _h, _i, file;
file = _d[_c]; _f = []; _h = files;
_b.push(compile(path.join(source, file))); for (_g = 0, _i = _h.length; _g < _i; _g++) {
file = _h[_g];
_f.push(compile(path.join(source, file)));
}
return _f;
});
} else if (path.extname(source) === '.coffee') {
fs.readFile(source, function(err, code) {
return compile_script(source, code, base);
});
if (options.watch) {
return watch(source, base);
}
} }
return _b;
}); });
} else if (source === base || path.extname(source) === '.coffee') { });
if (options.monitor) { };
puts('Compiling ' + source); return compile(source);
} })());
fs.readFile(source, function(err, code) {
return compile_script(source, code);
});
if (options.watch) {
return watch(source);
}
}
});
});
};
run = function run() {
var _b, _c, _d, _e, source;
_b = []; _d = sources;
for (_c = 0, _e = _d.length; _c < _e; _c++) {
source = _d[_c];
_b.push((function() {
base = source;
return compile(source);
})());
}
return _b;
};
run();
if (options.watch) {
return setInterval(run, 500);
} }
return _b;
}; };
// Compile a single source script, containing the given code, according to the // Compile a single source script, containing the given code, according to the
// requested options. Both compile_scripts and watch_scripts share this method // requested options. If evaluating the script directly sets `__filename`,
// in common. If evaluating the script directly sets `__filename`, `__dirname` // `__dirname` and `module.filename` to be correct relative to the script's path.
// and `module.filename` to be correct relative to the script's path. compile_script = function compile_script(source, code, base) {
compile_script = function compile_script(source, code) {
var code_opts, js, o; var code_opts, js, o;
o = options; o = options;
code_opts = compile_options(source); code_opts = compile_options(source);
@ -131,7 +115,7 @@
if (o.print) { if (o.print) {
return print(js); return print(js);
} else if (o.compile) { } else if (o.compile) {
return write_js(source, js); return write_js(source, js, base);
} else if (o.lint) { } else if (o.lint) {
return lint(js); return lint(js);
} }
@ -162,8 +146,7 @@
// Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every // Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every
// time the file is updated. May be used in combination with other options, // time the file is updated. May be used in combination with other options,
// such as `--lint` or `--print`. // such as `--lint` or `--print`.
watch = function watch(source) { watch = function watch(source, base) {
is_watched[source] = true;
return fs.watchFile(source, { return fs.watchFile(source, {
persistent: true, persistent: true,
interval: 500 interval: 500
@ -171,25 +154,33 @@
if (curr.mtime.getTime() === prev.mtime.getTime()) { if (curr.mtime.getTime() === prev.mtime.getTime()) {
return null; return null;
} }
if (options.monitor) { if (options.compile) {
puts('Recompiling ' + source); puts(("Compiled " + source));
} }
return fs.readFile(source, function(err, code) { return fs.readFile(source, function(err, code) {
return compile_script(source, code); return compile_script(source, code, base);
}); });
}); });
}; };
// Write out a JavaScript source file with the compiled code. By default, files // Write out a JavaScript source file with the compiled code. By default, files
// are written out in `cwd` as `.js` files with the same name, but the output // are written out in `cwd` as `.js` files with the same name, but the output
// directory can be customized with `--output`. // directory can be customized with `--output`.
write_js = function write_js(source, js) { write_js = function write_js(source, js, base) {
var dir, filename, js_path, src_dir; var base_dir, compile, dir, filename, js_path, src_dir;
filename = path.basename(source, path.extname(source)) + '.js'; filename = path.basename(source, path.extname(source)) + '.js';
src_dir = path.dirname(source); src_dir = path.dirname(source);
dir = options.output ? path.join(options.output, src_dir.substring(base.length)) : src_dir; base_dir = src_dir.substring(base.length);
dir = options.output ? path.join(options.output, base_dir) : src_dir;
js_path = path.join(dir, filename); js_path = path.join(dir, filename);
return exec(("mkdir -p " + dir), function(error, stdout, stderr) { compile = function compile() {
return fs.writeFile(js_path, js); return fs.writeFile(js_path, js);
};
return path.exists(dir, function(exists) {
if (exists) {
return compile();
} else {
return exec(("mkdir -p " + dir), compile);
}
}); });
}; };
// Pipe compiled JS through JSLint (requires a working `jsl` command), printing // Pipe compiled JS through JSLint (requires a working `jsl` command), printing

View File

@ -26,7 +26,6 @@ SWITCHES: [
['-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']
['-m', '--monitor', 'show a message every time a script is compiled']
['-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']
@ -40,8 +39,6 @@ SWITCHES: [
# Top-level objects shared by all the functions. # Top-level objects shared by all the functions.
options: {} options: {}
sources: [] sources: []
base: ''
is_watched: {}
option_parser: null option_parser: null
# Run `coffee` by parsing passed options and determining what action to take. # Run `coffee` by parsing passed options and determining what action to take.
@ -67,34 +64,25 @@ exports.run: ->
# compile them. If a directory is passed, recursively compile all source # compile them. If a directory is passed, recursively compile all source
# files in it and all subdirectories. # files in it and all subdirectories.
compile_scripts: -> compile_scripts: ->
compile: (source) -> for source in sources
return if is_watched[source] base: source
path.exists source, (exists) -> compile: (source) ->
throw new Error "File not found: $source" unless exists path.exists source, (exists) ->
fs.stat source, (err, stats) -> throw new Error "File not found: $source" unless exists
if stats.isDirectory() fs.stat source, (err, stats) ->
fs.readdir source, (err, files) -> if stats.isDirectory()
for file in files fs.readdir source, (err, files) ->
compile path.join(source, file) for file in files
else if source == base or path.extname(source) == '.coffee' compile path.join(source, file)
puts 'Compiling ' + source if options.monitor else if path.extname(source) is '.coffee'
fs.readFile source, (err, code) -> compile_script(source, code) fs.readFile source, (err, code) -> compile_script(source, code, base)
watch(source) if options.watch watch source, base if options.watch
compile source
run: ->
for source in sources
base = source
compile(source)
run()
if options.watch
setInterval run, 500
# Compile a single source script, containing the given code, according to the # Compile a single source script, containing the given code, according to the
# requested options. Both compile_scripts and watch_scripts share this method # requested options. If evaluating the script directly sets `__filename`,
# in common. If evaluating the script directly sets `__filename`, `__dirname` # `__dirname` and `module.filename` to be correct relative to the script's path.
# and `module.filename` to be correct relative to the script's path. compile_script: (source, code, base) ->
compile_script: (source, code) ->
o: options o: options
code_opts: compile_options source code_opts: compile_options source
try try
@ -104,7 +92,7 @@ compile_script: (source, code) ->
else else
js: CoffeeScript.compile code, code_opts js: CoffeeScript.compile code, code_opts
if o.print then print js if o.print then print js
else if o.compile then write_js source, js else if o.compile then write_js source, js, base
else if o.lint then lint js else if o.lint then lint js
catch err catch err
if o.watch then puts err.message else throw err if o.watch then puts err.message else throw err
@ -122,23 +110,24 @@ compile_stdio: ->
# Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every # Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every
# time the file is updated. May be used in combination with other options, # time the file is updated. May be used in combination with other options,
# such as `--lint` or `--print`. # such as `--lint` or `--print`.
watch: (source) -> watch: (source, base) ->
is_watched[source] = true
fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) -> fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
return if curr.mtime.getTime() is prev.mtime.getTime() return if curr.mtime.getTime() is prev.mtime.getTime()
puts 'Recompiling ' + source if options.monitor puts "Compiled $source" if options.compile
fs.readFile source, (err, code) -> compile_script(source, code) fs.readFile source, (err, code) -> compile_script(source, code, base)
# Write out a JavaScript source file with the compiled code. By default, files # Write out a JavaScript source file with the compiled code. By default, files
# are written out in `cwd` as `.js` files with the same name, but the output # are written out in `cwd` as `.js` files with the same name, but the output
# directory can be customized with `--output`. # directory can be customized with `--output`.
write_js: (source, js) -> write_js: (source, js, base) ->
filename: path.basename(source, path.extname(source)) + '.js' filename: path.basename(source, path.extname(source)) + '.js'
src_dir: path.dirname(source) src_dir: path.dirname source
dir: if options.output then \ base_dir: src_dir.substring base.length
path.join options.output, src_dir.substring(base.length) else src_dir dir: if options.output then path.join options.output, base_dir else src_dir
js_path: path.join dir, filename js_path: path.join dir, filename
exec "mkdir -p $dir", (error, stdout, stderr) -> fs.writeFile js_path, js compile: -> fs.writeFile js_path, js
path.exists dir, (exists) ->
if exists then compile() else exec "mkdir -p $dir", compile
# Pipe compiled JS through JSLint (requires a working `jsl` command), printing # Pipe compiled JS through JSLint (requires a working `jsl` command), printing
# any errors or warnings that arise. # any errors or warnings that arise.