From d64b8fd9d82c57f624ccd61778a3ee4d6286e44e Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Tue, 4 May 2010 23:22:28 -0400 Subject: [PATCH] merged in Trevor Burnham's recursive coffee compilation, with some adjustments... --- lib/command.js | 121 +++++++++++++++++++++------------------------ src/command.coffee | 67 +++++++++++-------------- 2 files changed, 84 insertions(+), 104 deletions(-) diff --git a/lib/command.js b/lib/command.js index 1d5aa0e5..7ea2542d 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,5 +1,5 @@ (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 // into various forms: saved into `.js` files or printed to stdout, piped to // [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. 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. - 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. options = {}; sources = []; - base = ''; - is_watched = {}; option_parser = null; // Run `coffee` by parsing passed options and determining what action to take. // 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 // files in it and all subdirectories. compile_scripts = function compile_scripts() { - var compile, run; - compile = function compile(source) { - if (is_watched[source]) { - return null; - } - return path.exists(source, function(exists) { - if (!(exists)) { - throw new Error(("File not found: " + source)); - } - return fs.stat(source, function(err, stats) { - if (stats.isDirectory()) { - return fs.readdir(source, function(err, files) { - var _b, _c, _d, _e, file; - _b = []; _d = files; - for (_c = 0, _e = _d.length; _c < _e; _c++) { - file = _d[_c]; - _b.push(compile(path.join(source, file))); + var _b, _c, _d, _e, base, compile, source; + _b = []; _d = sources; + for (_c = 0, _e = _d.length; _c < _e; _c++) { + source = _d[_c]; + _b.push((function() { + base = source; + compile = function compile(source) { + return path.exists(source, function(exists) { + if (!(exists)) { + throw new Error(("File not found: " + source)); + } + return fs.stat(source, function(err, stats) { + if (stats.isDirectory()) { + return fs.readdir(source, function(err, files) { + var _f, _g, _h, _i, file; + _f = []; _h = files; + 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); - } - 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 compile(source); + })()); } + return _b; }; // Compile a single source script, containing the given code, according to the - // requested options. Both compile_scripts and watch_scripts share this method - // in common. If evaluating the script directly sets `__filename`, `__dirname` - // and `module.filename` to be correct relative to the script's path. - compile_script = function compile_script(source, code) { + // requested options. If evaluating the script directly sets `__filename`, + // `__dirname` and `module.filename` to be correct relative to the script's path. + compile_script = function compile_script(source, code, base) { var code_opts, js, o; o = options; code_opts = compile_options(source); @@ -131,7 +115,7 @@ if (o.print) { return print(js); } else if (o.compile) { - return write_js(source, js); + return write_js(source, js, base); } else if (o.lint) { return lint(js); } @@ -162,8 +146,7 @@ // Watch a source CoffeeScript file using `fs.watchFile`, recompiling it every // time the file is updated. May be used in combination with other options, // such as `--lint` or `--print`. - watch = function watch(source) { - is_watched[source] = true; + watch = function watch(source, base) { return fs.watchFile(source, { persistent: true, interval: 500 @@ -171,25 +154,33 @@ if (curr.mtime.getTime() === prev.mtime.getTime()) { return null; } - if (options.monitor) { - puts('Recompiling ' + source); + if (options.compile) { + puts(("Compiled " + source)); } 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 // are written out in `cwd` as `.js` files with the same name, but the output // directory can be customized with `--output`. - write_js = function write_js(source, js) { - var dir, filename, js_path, src_dir; + write_js = function write_js(source, js, base) { + var base_dir, compile, dir, filename, js_path, src_dir; filename = path.basename(source, path.extname(source)) + '.js'; 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); - return exec(("mkdir -p " + dir), function(error, stdout, stderr) { + compile = function compile() { 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 diff --git a/src/command.coffee b/src/command.coffee index 456d18f9..9fec4efc 100644 --- a/src/command.coffee +++ b/src/command.coffee @@ -26,7 +26,6 @@ SWITCHES: [ ['-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'] @@ -40,8 +39,6 @@ SWITCHES: [ # Top-level objects shared by all the functions. options: {} sources: [] -base: '' -is_watched: {} option_parser: null # 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 # files in it and all subdirectories. compile_scripts: -> - compile: (source) -> - return if is_watched[source] - path.exists source, (exists) -> - throw new Error "File not found: $source" unless exists - fs.stat source, (err, stats) -> - if stats.isDirectory() - fs.readdir source, (err, files) -> - for file in files - compile path.join(source, file) - else if source == base or path.extname(source) == '.coffee' - puts 'Compiling ' + source if options.monitor - fs.readFile source, (err, code) -> compile_script(source, code) - watch(source) if options.watch - - run: -> - for source in sources - base = source - compile(source) - - run() - if options.watch - setInterval run, 500 + for source in sources + base: source + compile: (source) -> + path.exists source, (exists) -> + throw new Error "File not found: $source" unless exists + fs.stat source, (err, stats) -> + if stats.isDirectory() + fs.readdir source, (err, files) -> + for file in files + compile path.join(source, file) + else if path.extname(source) is '.coffee' + fs.readFile source, (err, code) -> compile_script(source, code, base) + watch source, base if options.watch + compile source # Compile a single source script, containing the given code, according to the -# requested options. Both compile_scripts and watch_scripts share this method -# in common. If evaluating the script directly sets `__filename`, `__dirname` -# and `module.filename` to be correct relative to the script's path. -compile_script: (source, code) -> +# requested options. If evaluating the script directly sets `__filename`, +# `__dirname` and `module.filename` to be correct relative to the script's path. +compile_script: (source, code, base) -> o: options code_opts: compile_options source try @@ -104,7 +92,7 @@ compile_script: (source, code) -> else js: CoffeeScript.compile code, code_opts 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 catch 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 # time the file is updated. May be used in combination with other options, # such as `--lint` or `--print`. -watch: (source) -> - is_watched[source] = true +watch: (source, base) -> fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) -> return if curr.mtime.getTime() is prev.mtime.getTime() - puts 'Recompiling ' + source if options.monitor - fs.readFile source, (err, code) -> compile_script(source, code) + puts "Compiled $source" if options.compile + fs.readFile source, (err, code) -> compile_script(source, code, base) # 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 # directory can be customized with `--output`. -write_js: (source, js) -> +write_js: (source, js, base) -> filename: path.basename(source, path.extname(source)) + '.js' - src_dir: path.dirname(source) - dir: if options.output then \ - path.join options.output, src_dir.substring(base.length) else src_dir + src_dir: path.dirname source + base_dir: src_dir.substring base.length + dir: if options.output then path.join options.output, base_dir else src_dir 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 # any errors or warnings that arise.