diff --git a/Cakefile b/Cakefile
index d188bd2c..31809166 100644
--- a/Cakefile
+++ b/Cakefile
@@ -5,10 +5,16 @@ CoffeeScript = require './lib/coffee-script'
{spawn, exec} = require 'child_process'
# ANSI Terminal Colors.
-bold = '\033[0;1m'
-red = '\033[0;31m'
-green = '\033[0;32m'
-reset = '\033[0m'
+enableColors = no
+unless process.platform is 'win32'
+ enableColors = not process.env.NODE_DISABLE_COLORS
+
+bold = red = green = reset = ''
+if enableColors
+ bold = '\033[0;1m'
+ red = '\033[0;31m'
+ green = '\033[0;32m'
+ reset = '\033[0m'
# Built file header.
header = """
@@ -98,11 +104,19 @@ task 'build:browser', 'rebuild the merged script for inclusion in the browser',
};
"""
code = """
- this.CoffeeScript = function() {
- function require(path){ return require[path]; }
- #{code}
- return require['./coffee-script']
- }()
+ (function(root) {
+ var CoffeeScript = function() {
+ function require(path){ return require[path]; }
+ #{code}
+ return require['./coffee-script'];
+ }();
+
+ if (typeof define === 'function' && define.amd) {
+ define(function() { return CoffeeScript; });
+ } else {
+ root.CoffeeScript = CoffeeScript;
+ }
+ }(this));
"""
unless process.env.MINIFY is 'false'
{parser, uglify} = require 'uglify-js'
@@ -155,19 +169,9 @@ runTests = (CoffeeScript) ->
passedTests = 0
failures = []
- # Make "global" reference available to tests
- global.global = global
-
- # Mix in the assert module globally, to make it available for tests.
- addGlobal = (name, func) ->
- global[name] = ->
- passedTests += 1
- func arguments...
-
- addGlobal name, func for name, func of require 'assert'
+ global[name] = func for name, func of require 'assert'
# Convenience aliases.
- global.eq = global.strictEqual
global.CoffeeScript = CoffeeScript
# Our test helper function for delimiting different test cases.
@@ -175,26 +179,29 @@ runTests = (CoffeeScript) ->
try
fn.test = {description, currentFile}
fn.call(fn)
+ ++passedTests
catch e
e.description = description if description?
e.source = fn.toString() if fn.toString?
failures.push filename: currentFile, error: e
- # A recursive functional equivalence helper; uses egal for testing equivalence.
# See http://wiki.ecmascript.org/doku.php?id=harmony:egal
- arrayEqual = (a, b) ->
+ egal = (a, b) ->
if a is b
- # 0 isnt -0
a isnt 0 or 1/a is 1/b
- else if a instanceof Array and b instanceof Array
- return no unless a.length is b.length
- return no for el, idx in a when not arrayEqual el, b[idx]
- yes
else
- # NaN is NaN
a isnt a and b isnt b
- global.arrayEq = (a, b, msg) -> ok arrayEqual(a,b), msg
+ # A recursive functional equivalence helper; uses egal for testing equivalence.
+ arrayEgal = (a, b) ->
+ if egal a, b then yes
+ else if a instanceof Array and b instanceof Array
+ return no unless a.length is b.length
+ return no for el, idx in a when not arrayEgal el, b[idx]
+ yes
+
+ global.eq = (a, b, msg) -> ok egal(a, b), msg
+ global.arrayEq = (a, b, msg) -> ok arrayEgal(a,b), msg
# When all the tests have run, collect and print errors.
# If a stacktrace is available, output the compiled function source.
diff --git a/documentation/coffee/array_comprehensions.coffee b/documentation/coffee/array_comprehensions.coffee
index 47e3f54f..6069ac53 100644
--- a/documentation/coffee/array_comprehensions.coffee
+++ b/documentation/coffee/array_comprehensions.coffee
@@ -1,10 +1,10 @@
# Eat lunch.
eat food for food in ['toast', 'cheese', 'wine']
-# Fine dining
-courses = ['salad', 'entree', 'dessert']
-menu course + 1, dish for dish, course in courses
+# Fine five course dining.
+courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
+menu i + 1, dish for dish, i in courses
-# Health conscious meal
+# Health conscious meal.
foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate'
diff --git a/documentation/coffee/block_comment.coffee b/documentation/coffee/block_comment.coffee
index dba86b6c..31ca0bd5 100644
--- a/documentation/coffee/block_comment.coffee
+++ b/documentation/coffee/block_comment.coffee
@@ -1,5 +1,5 @@
###
-CoffeeScript Compiler v1.1.2
+CoffeeScript Compiler v1.2.0
Released under the MIT License
###
diff --git a/documentation/coffee/embedded.coffee b/documentation/coffee/embedded.coffee
index bb16b79d..664874af 100644
--- a/documentation/coffee/embedded.coffee
+++ b/documentation/coffee/embedded.coffee
@@ -3,3 +3,4 @@ hi = `function() {
}`
+
diff --git a/documentation/coffee/existence.coffee b/documentation/coffee/existence.coffee
index 1cc6725c..a42dd668 100644
--- a/documentation/coffee/existence.coffee
+++ b/documentation/coffee/existence.coffee
@@ -8,3 +8,5 @@ footprints = yeti ? "bear"
+
+
\ No newline at end of file
diff --git a/documentation/coffee/heredocs.coffee b/documentation/coffee/heredocs.coffee
index df3a0161..1d74df4b 100644
--- a/documentation/coffee/heredocs.coffee
+++ b/documentation/coffee/heredocs.coffee
@@ -1,7 +1,6 @@
-html = '''
+html = """
cup of coffeescript
- '''
-
-
+ """
+
diff --git a/documentation/coffee/interpolation.coffee b/documentation/coffee/interpolation.coffee
index 7516f17e..78a7a183 100644
--- a/documentation/coffee/interpolation.coffee
+++ b/documentation/coffee/interpolation.coffee
@@ -4,3 +4,6 @@ quote = "A picture is a fact. -- #{ author }"
sentence = "#{ 22 / 7 } is a decent approximation of π"
+
+
+
diff --git a/documentation/coffee/multiple_return_values.coffee b/documentation/coffee/multiple_return_values.coffee
index b4e43693..3cef9280 100644
--- a/documentation/coffee/multiple_return_values.coffee
+++ b/documentation/coffee/multiple_return_values.coffee
@@ -5,3 +5,5 @@ weatherReport = (location) ->
[city, temp, forecast] = weatherReport "Berkeley, CA"
+
+
diff --git a/documentation/coffee/object_extraction.coffee b/documentation/coffee/object_extraction.coffee
index 07ac335a..a2d498e8 100644
--- a/documentation/coffee/object_extraction.coffee
+++ b/documentation/coffee/object_extraction.coffee
@@ -11,3 +11,4 @@ futurists =
{poet: {name, address: [street, city]}} = futurists
+
diff --git a/documentation/coffee/parallel_assignment.coffee b/documentation/coffee/parallel_assignment.coffee
index 2b3fd348..4de9dc0e 100644
--- a/documentation/coffee/parallel_assignment.coffee
+++ b/documentation/coffee/parallel_assignment.coffee
@@ -4,3 +4,6 @@ theSwitch = 0
[theBait, theSwitch] = [theSwitch, theBait]
+
+
+
\ No newline at end of file
diff --git a/documentation/coffee/patterns_and_splats.coffee b/documentation/coffee/patterns_and_splats.coffee
index 12eb818f..dedf683a 100644
--- a/documentation/coffee/patterns_and_splats.coffee
+++ b/documentation/coffee/patterns_and_splats.coffee
@@ -5,3 +5,5 @@ tag = " Override exported methods for non-Node.js engines. Use standard JavaScript Running code does not provide access to this scope. Use standard JavaScript Running code does not provide access to this scope. If we're not in a browser environment, we're finished with the public API. Load a remote script from the current domain via XHR. If we're not in a browser environment, we're finished with the public API. Load a remote script from the current domain via XHR. Mixin the top-level Cake functions for Cakefiles to use directly. Define a Cake task with a short name, an optional sentence description,
-and the function to run as the action itself. Define an option that the Cakefile accepts. The parsed options hash,
containing all of the command-line options passed, will be made available
-as the first argument to the action. Invoke another task in the current Cakefile. Invoke another task in the current Cakefile. Run Display the list of Cake tasks in a format similar to Display the list of Cake tasks in a format similar to Print an error and exit when attempting to call an undefined task. Print an error and exit when attempting to use an invalid task/option. When
browser.coffee
CoffeeScript = require './coffee-script'
-CoffeeScript.require = require
eval to eval code. CoffeeScript.eval = (code, options) ->
- eval CoffeeScript.compile code, options
CoffeeScript.run = (code, options = {}) ->
+CoffeeScript.require = require
eval to eval code. CoffeeScript.eval = (code, options) ->
+ eval CoffeeScript.compile code, options
CoffeeScript.run = (code, options = {}) ->
options.bare = on
- Function(CoffeeScript.compile code, options)()
return unless window?
CoffeeScript.load = (url, callback) ->
+ Function(CoffeeScript.compile code, options)()
return unless window?
CoffeeScript.load = (url, callback) ->
xhr = new (window.ActiveXObject or XMLHttpRequest)('Microsoft.XMLHTTP')
xhr.open 'GET', url, true
xhr.overrideMimeType 'text/plain' if 'overrideMimeType' of xhr
diff --git a/documentation/docs/cake.html b/documentation/docs/cake.html
index 3824c8d9..69049f64 100644
--- a/documentation/docs/cake.html
+++ b/documentation/docs/cake.html
@@ -12,32 +12,45 @@ current directory's Cakefile.
options = {}
switches = []
oparse = null helpers.extend global,
task: (name, description, action) ->
+and the function to run as the action itself.
task: (name, description, action) ->
[action, description] = [description, action] unless action
tasks[name] = {name, description, action}
option: (letter, flag, description) ->
- switches.push [letter, flag, description]
invoke: (name) ->
+as the first argument to the action.
option: (letter, flag, description) ->
+ switches.push [letter, flag, description]
invoke: (name) ->
missingTask name unless tasks[name]
tasks[name].action options
cake. Executes all of the tasks you pass, in order. Note that Node's
asynchrony may cause tasks to execute in a different order than you'd expect.
-If no tasks are passed, print the help screen. exports.run = ->
- path.exists 'Cakefile', (exists) ->
- throw new Error("Cakefile not found in #{process.cwd()}") unless exists
- args = process.argv.slice 2
- CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile'
- oparse = new optparse.OptionParser switches
- return printTasks() unless args.length
+If no tasks are passed, print the help screen. Keep a reference to the
+original directory name, when running Cake tasks from subdirectories.
exports.run = ->
+ global.__originalDirname = fs.realpathSync '.'
+ process.chdir cakefileDirectory __originalDirname
+ args = process.argv.slice 2
+ CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile'
+ oparse = new optparse.OptionParser switches
+ return printTasks() unless args.length
+ try
options = oparse.parse(args)
- invoke arg for arg in options.arguments
rake -T printTasks = ->
- console.log ''
+ catch e
+ return fatalError "#{e}"
+ invoke arg for arg in options.arguments
rake -T printTasks = ->
+ cakefilePath = path.join path.relative(__originalDirname, process.cwd()), 'Cakefile'
+ console.log "#{cakefilePath} defines the following tasks:\n"
for name, task of tasks
spaces = 20 - name.length
spaces = if spaces > 0 then Array(spaces + 1).join(' ') else ''
desc = if task.description then "# #{task.description}" else ''
console.log "cake #{name}#{spaces} #{desc}"
- console.log oparse.help() if switches.length
missingTask = (task) ->
- console.log "No such task: \"#{task}\""
+ console.log oparse.help() if switches.length
fatalError = (message) ->
+ console.error message + '\n'
+ console.log 'To see a list of all tasks/options, run "cake"'
process.exit 1
+missingTask = (task) -> fatalError "No such task: #{task}"
cake is invoked, search in the current and all parent directories
+to find the relevant Cakefile. cakefileDirectory = (dir) ->
+ return dir if path.existsSync path.join dir, 'Cakefile'
+ parent = path.normalize path.join dir, '..'
+ return cakefileDirectory parent unless parent is dir
+ throw new Error "Cakefile not found in #{process.cwd()}"
+
If included on a webpage, it will automatically sniff out, compile, and
execute all scripts present in text/coffeescript tags.
fs = require 'fs'
path = require 'path'
-{Script} = require 'vm'
-Module = require 'module'
{Lexer,RESERVED} = require './lexer'
-{parser} = require './parser'TODO: Remove registerExtension when fully deprecated.
if require.extensions
- require.extensions['.coffee'] = (module, filename) ->
+{parser} = require './parser'
+vm = require 'vm'TODO: Remove registerExtension when fully deprecated.
if require.extensions
+ require.extensions['.coffee'] = (module, filename) ->
content = compile fs.readFileSync(filename, 'utf8'), {filename}
module._compile content, filename
else if require.registerExtension
- require.registerExtension '.coffee', (content) -> compile contentThe current CoffeeScript version number.
exports.VERSION = '1.1.2'Words that cannot be used as identifiers in CoffeeScript code
exports.RESERVED = RESERVEDExpose helpers for testing.
exports.helpers = require './helpers'Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison -compiler.
exports.compile = compile = (code, options = {}) ->
+ require.registerExtension '.coffee', (content) -> compile contentThe current CoffeeScript version number.
exports.VERSION = '1.2.0'Words that cannot be used as identifiers in CoffeeScript code
exports.RESERVED = RESERVEDExpose helpers for testing.
exports.helpers = require './helpers'Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison +compiler.
exports.compile = compile = (code, options = {}) ->
+ {merge} = exports.helpers
try
- (parser.parse lexer.tokenize code).compile options
+ (parser.parse lexer.tokenize code).compile merge {}, options
catch err
err.message = "In #{options.filename}, #{err.message}" if options.filename
- throw errTokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens = (code, options) ->
+ throw errTokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens = (code, options) ->
lexer.tokenize code, optionsParse a string of CoffeeScript code or an array of lexed tokens, and
return the AST. You can then compile it by calling .compile() on the root,
-or traverse it by using .traverse() with a callback.
exports.nodes = (source, options) ->
+or traverse it by using .traverseChildren() with a callback. exports.nodes = (source, options) ->
if typeof source is 'string'
parser.parse lexer.tokenize source, options
else
parser.parse sourceCompile and execute a string of CoffeeScript (on the server), correctly
-setting __filename, __dirname, and relative require().
exports.run = (code, options) ->
+setting __filename, __dirname, and relative require(). exports.run = (code, options) ->
mainModule = require.mainSet the filename.
mainModule.filename = process.argv[1] =
- if options.filename then fs.realpathSync(options.filename) else '.'Clear the module cache.
mainModule.moduleCache and= {}Assign paths for node_modules loading
if process.binding('natives').module
- {Module} = require 'module'
- mainModule.paths = Module._nodeModulePaths path.dirname options.filenameCompile.
if path.extname(mainModule.filename) isnt '.coffee' or require.extensions
+ if options.filename then fs.realpathSync(options.filename) else '.'Clear the module cache.
mainModule.moduleCache and= {}Assign paths for node_modules loading
mainModule.paths = require('module')._nodeModulePaths path.dirname options.filenameCompile.
if path.extname(mainModule.filename) isnt '.coffee' or require.extensions
mainModule._compile compile(code, options), mainModule.filename
else
mainModule._compile code, mainModule.filenameCompile and evaluate a string of CoffeeScript (in a Node.js-like environment). -The CoffeeScript REPL uses this to run the input.
exports.eval = (code, options = {}) ->
+The CoffeeScript REPL uses this to run the input. exports.eval = (code, options = {}) ->
return unless code = code.trim()
- sandbox = Script.createContext()
- sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox
- if options.sandbox?
- if options.sandbox instanceof sandbox.constructor
- sandbox = options.sandbox
+ Script = vm.Script
+ if Script
+ if options.sandbox?
+ if options.sandbox instanceof Script.createContext().constructor
+ sandbox = options.sandbox
+ else
+ sandbox = Script.createContext()
+ sandbox[k] = v for own k, v of options.sandbox
+ sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox
else
- sandbox[k] = v for own k, v of options.sandbox
- sandbox.__filename = options.filename || 'eval'
- sandbox.__dirname = path.dirname sandbox.__filenamedefine module/require only if they chose not to specify their own
unless sandbox.module or sandbox.require
- Module = require 'module'
- sandbox.module = _module = new Module(options.modulename || 'eval')
- sandbox.require = _require = (path) -> Module._load path, _module
- _module.filename = sandbox.__filename
- _require[r] = require[r] for r in Object.getOwnPropertyNames requireuse the same hack node currently uses for their own REPL
_require.paths = _module.paths = Module._nodeModulePaths process.cwd()
- _require.resolve = (request) -> Module._resolveFilename request, _module
+ sandbox = global
+ sandbox.__filename = options.filename || 'eval'
+ sandbox.__dirname = path.dirname sandbox.__filenamedefine module/require only if they chose not to specify their own
unless sandbox isnt global or sandbox.module or sandbox.require
+ Module = require 'module'
+ sandbox.module = _module = new Module(options.modulename || 'eval')
+ sandbox.require = _require = (path) -> Module._load path, _module, true
+ _module.filename = sandbox.__filename
+ _require[r] = require[r] for r in Object.getOwnPropertyNames require when r isnt 'paths'use the same hack node currently uses for their own REPL
_require.paths = _module.paths = Module._nodeModulePaths process.cwd()
+ _require.resolve = (request) -> Module._resolveFilename request, _module
o = {}
o[k] = v for own k, v of options
o.bare = on # ensure return value
js = compile code, o
- Script.runInContext js, sandboxInstantiate a Lexer for our use here.
lexer = new LexerThe real Lexer produces a generic stream of tokens. This object provides a + if sandbox is global + vm.runInThisContext js + else + vm.runInContext js, sandbox
Instantiate a Lexer for our use here.
lexer = new LexerThe real Lexer produces a generic stream of tokens. This object provides a thin wrapper around it, compatible with the Jison API. We can then pass it directly as a "Jison lexer".
parser.lexer =
- lex: ->
+ lex: ->
[tag, @yytext, @yylineno] = @tokens[@pos++] or ['']
tag
- setInput: (@tokens) ->
+ setInput: (@tokens) ->
@pos = 0
- upcomingInput: ->
+ upcomingInput: ->
""
parser.yy = require './nodes'
diff --git a/documentation/docs/command.html b/documentation/docs/command.html
index 955bceb4..61843ed3 100644
--- a/documentation/docs/command.html
+++ b/documentation/docs/command.html
@@ -10,29 +10,33 @@ interactive REPL. Allow CoffeeScript to emit Node.js events.
helpers.extend CoffeeScript, new EventEmitter
-printLine = (line) -> process.stdout.write line + '\n'
-printWarn = (line) -> process.binding('stdio').writeError line + '\n'The help banner that is printed when coffee is called without arguments.
BANNER = '''
+printLine = (line) -> process.stdout.write line + '\n'
+printWarn = (line) -> process.stderr.write line + '\n'The help banner that is printed when coffee is called without arguments.
BANNER = '''
Usage: coffee [options] path/to/script.coffee
+
+ If called without options, `coffee` will run your script.
'''The list of all the valid option flags that coffee knows how to handle.
SWITCHES = [
+ ['-b', '--bare', 'compile without a top-level function wrapper']
['-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']
- ['-j', '--join [FILE]', 'concatenate the scripts before compiling']
- ['-w', '--watch', 'watch scripts for changes, and recompile']
- ['-p', '--print', 'print the compiled JavaScript to stdout']
- ['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint']
- ['-s', '--stdio', 'listen for and compile scripts over stdio']
- ['-e', '--eval', 'compile a string from the command line']
- ['-r', '--require [FILE*]', 'require a library before executing your script']
- ['-b', '--bare', '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']
- [ '--nodejs [ARGS]', 'pass options through to the "node" binary']
- ['-v', '--version', 'display CoffeeScript version']
+ ['-e', '--eval', 'pass a string from the command line as input']
['-h', '--help', 'display this help message']
+ ['-i', '--interactive', 'run an interactive CoffeeScript REPL']
+ ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling']
+ ['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint']
+ ['-n', '--nodes', 'print out the parse tree that the parser produces']
+ [ '--nodejs [ARGS]', 'pass options directly to the "node" binary']
+ ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript']
+ ['-p', '--print', 'print out the compiled JavaScript']
+ ['-r', '--require [FILE*]', 'require a library before executing your script']
+ ['-s', '--stdio', 'listen for and compile scripts over stdio']
+ ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce']
+ ['-v', '--version', 'display the version number']
+ ['-w', '--watch', 'watch scripts for changes and rerun commands']
]Top-level objects shared by all the functions.
opts = {}
sources = []
-contents = []
+sourceCode = []
+notSources = {}
+watchers = {}
optionParser = nullRun coffee by parsing passed options and determining what action to take.
Many flags cause us to divert before compiling anything. Flags passed after
-- will be passed verbatim to your script as arguments in process.argv
exports.run = ->
@@ -42,57 +46,51 @@ Many flags cause us to divert before compiling anything. Flags passed after
return version() if opts.version
loadRequires() if opts.require
return require './repl' if opts.interactive
+ if opts.watch and !fs.watch
+ printWarn "The --watch feature depends on Node v0.6.0+. You are running #{process.version}."
return compileStdio() if opts.stdio
return compileScript null, sources[0] if opts.eval
return require './repl' unless sources.length
if opts.run
opts.literals = sources.splice(1).concat opts.literals
- process.ARGV = process.argv = process.argv.slice(0, 2).concat opts.literals
+ process.argv = process.argv.slice(0, 2).concat opts.literals
process.argv[0] = 'coffee'
process.execPath = require.main.filename
- compileScripts()Asynchronously read in each CoffeeScript in a list of source files and -compile them. If a directory is passed, recursively compile all -'.coffee' extension source files in it and all subdirectories.
compileScripts = ->
- unprocessed = []
for source in sources
- unprocessed[sources.indexOf(source)]=1
- for source in sources
- base = path.join(source)
- compile = (source, sourceIndex, topLevel) ->
- remaining_files = ->
- total = 0
- total += x for x in unprocessed
- total
- path.exists source, (exists) ->
- if topLevel and not exists and source[-7..] isnt '.coffee'
- return compile "#{source}.coffee", sourceIndex, topLevel
-
- throw new Error "File not found: #{source}" if topLevel and not exists
- fs.stat source, (err, stats) ->
- throw err if err
- if stats.isDirectory()
- fs.readdir source, (err, files) ->
- throw err if err
- unprocessed[sourceIndex] += files.length
- for file in files
- compile path.join(source, file), sourceIndex
- unprocessed[sourceIndex] -= 1
- else if topLevel or path.extname(source) is '.coffee'
- fs.readFile source, (err, code) ->
- throw err if err
- unprocessed[sourceIndex] -= 1
- if opts.join
- contents[sourceIndex] = helpers.compact([contents[sourceIndex], code.toString()]).join('\n')
- if helpers.compact(contents).length > 0 and remaining_files() == 0
- compileJoin()
- else
- compileScript(source, code.toString(), base)
- watch source, base if opts.watch and not opts.join
- else
- unprocessed[sourceIndex] -= 1
- compile source, sources.indexOf(source), trueCompile a single source script, containing the given code, according to the + compilePath source, yes, path.normalize source
Compile a path, which could be a script or a directory. If a directory +is passed, recursively compile all '.coffee' extension source files in it +and all subdirectories.
compilePath = (source, topLevel, base) ->
+ fs.stat source, (err, stats) ->
+ throw err if err and err.code isnt 'ENOENT'
+ if err?.code is 'ENOENT'
+ if topLevel and source[-7..] isnt '.coffee'
+ source = sources[sources.indexOf(source)] = "#{source}.coffee"
+ return compilePath source, topLevel, base
+ if topLevel
+ console.error "File not found: #{source}"
+ process.exit 1
+ return
+ if stats.isDirectory()
+ watchDir source, base if opts.watch
+ fs.readdir source, (err, files) ->
+ throw err if err and err.code isnt 'ENOENT'
+ return if err?.code is 'ENOENT'
+ files = files.map (file) -> path.join source, file
+ index = sources.indexOf source
+ sources[index..index] = files
+ sourceCode[index..index] = files.map -> null
+ compilePath file, no, base for file in files
+ else if topLevel or path.extname(source) is '.coffee'
+ watch source, base if opts.watch
+ fs.readFile source, (err, code) ->
+ throw err if err and err.code isnt 'ENOENT'
+ return if err?.code is 'ENOENT'
+ compileScript(source, code.toString(), base)
+ else
+ notSources[source] = yes
+ removeSource source, baseCompile a single source script, containing the given code, according to the
requested options. If evaluating the script directly sets __filename,
-__dirname and module.filename to be correct relative to the script's path.
compileScript = (file, input, base) ->
+__dirname and module.filename to be correct relative to the script's path. compileScript = (file, input, base) ->
o = opts
options = compileOptions file
try
@@ -101,6 +99,9 @@ requested options. If evaluating the script directly sets __filenameif o.tokens then printTokens CoffeeScript.tokens t.input
else if o.nodes then printLine CoffeeScript.nodes(t.input).toString().trim()
else if o.run then CoffeeScript.run t.input, t.options
+ else if o.join and t.file isnt o.join
+ sourceCode[sources.indexOf(t.file)] = t.input
+ compileJoin()
else
t.output = CoffeeScript.compile t.input, t.options
CoffeeScript.emit 'success', task
@@ -111,74 +112,157 @@ requested options. If evaluating the script directly sets __filenameCoffeeScript.emit 'failure', err, task
return if CoffeeScript.listeners('failure').length
return printLine err.message if o.watch
- printWarn err.stack
+ printWarn err instanceof Error and err.stack or "ERROR: #{err}"
process.exit 1Attach the appropriate listeners to compile scripts incoming over stdin, and write them back to stdout.
compileStdio = ->
code = ''
stdin = process.openStdin()
- stdin.on 'data', (buffer) ->
+ stdin.on 'data', (buffer) ->
code += buffer.toString() if buffer
stdin.on 'end', ->
- compileScript null, codeAfter all of the source files are done being read, concatenate and compile -them together.
compileJoin = ->
- code = contents.join '\n'
- compileScript opts.join, code, opts.joinLoad files that are to-be-required before compilation occurs.
loadRequires = ->
+ compileScript null, codeIf all of the source files are done being read, concatenate and compile +them together.
joinTimeout = null
+compileJoin = ->
+ return unless opts.join
+ unless sourceCode.some((code) -> code is null)
+ clearTimeout joinTimeout
+ joinTimeout = wait 100, ->
+ compileScript opts.join, sourceCode.join('\n'), opts.joinLoad files that are to-be-required before compilation occurs.
loadRequires = ->
realFilename = module.filename
module.filename = '.'
require req for req in opts.require
- module.filename = realFilenameWatch a source CoffeeScript file using fs.watchFile, recompiling it every
+ module.filename = realFilename
Watch a source CoffeeScript file using fs.watch, recompiling it every
time the file is updated. May be used in combination with other options,
-such as --lint or --print.
watch = (source, base) ->
- fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
- return if curr.size is prev.size and curr.mtime.getTime() is prev.mtime.getTime()
- fs.readFile source, (err, code) ->
- throw err if err
- compileScript(source, code.toString(), 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.
writeJs = (source, js, base) ->
+such as --lint or --print. watch = (source, base) ->
+
+ prevStats = null
+ compileTimeout = null
+
+ watchErr = (e) ->
+ if e.code is 'ENOENT'
+ return if sources.indexOf(source) is -1
+ removeSource source, base, yes
+ compileJoin()
+ else throw e
+
+ compile = ->
+ clearTimeout compileTimeout
+ compileTimeout = wait 25, ->
+ fs.stat source, (err, stats) ->
+ return watchErr err if err
+ return if prevStats and (stats.size is prevStats.size and
+ stats.mtime.getTime() is prevStats.mtime.getTime())
+ prevStats = stats
+ fs.readFile source, (err, code) ->
+ return watchErr err if err
+ compileScript(source, code.toString(), base)
+
+ watchErr = (e) ->
+ throw e unless e.code is 'ENOENT'
+ removeSource source, base, yes
+ compileJoin()
+
+ try
+ watcher = fs.watch source, callback = (event) ->
+ if event is 'change'
+ compile()
+ else if event is 'rename'
+ watcher.close()
+ wait 250, ->
+ compile()
+ try
+ watcher = fs.watch source, callback
+ catch e
+ watchErr e
+ catch e
+ watchErr eWatch a directory of files for new additions.
watchDir = (source, base) ->
+ readdirTimeout = null
+ try
+ watcher = fs.watch source, ->
+ clearTimeout readdirTimeout
+ readdirTimeout = wait 25, ->
+ fs.readdir source, (err, files) ->
+ if err
+ throw err unless err.code is 'ENOENT'
+ watcher.close()
+ return unwatchDir source, base
+ files = files.map (file) -> path.join source, file
+ for file in files when not notSources[file]
+ continue if sources.some (s) -> s.indexOf(file) >= 0
+ sources.push file
+ sourceCode.push null
+ compilePath file, no, base
+ catch e
+ throw e unless e.code is 'ENOENT'
+
+unwatchDir = (source, base) ->
+ prevSources = sources.slice()
+ toRemove = (file for file in sources when file.indexOf(source) >= 0)
+ removeSource file, base, yes for file in toRemove
+ return unless sources.some (s, i) -> prevSources[i] isnt s
+ compileJoin()Remove a file from our source list, and source code cache. Optionally remove +the compiled JS version as well.
removeSource = (source, base, removeJs) ->
+ index = sources.indexOf source
+ sources.splice index, 1
+ sourceCode.splice index, 1
+ if removeJs and not opts.join
+ jsPath = outputPath source, base
+ path.exists jsPath, (exists) ->
+ if exists
+ fs.unlink jsPath, (err) ->
+ throw err if err and err.code isnt 'ENOENT'
+ timeLog "removed #{source}"Get the corresponding output JavaScript path for a source file.
outputPath = (source, base) ->
filename = path.basename(source, path.extname(source)) + '.js'
srcDir = path.dirname source
baseDir = if base is '.' then srcDir else srcDir.substring base.length
dir = if opts.output then path.join opts.output, baseDir else srcDir
- jsPath = path.join dir, filename
- compile = ->
+ path.join dir, filenameWrite 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.
writeJs = (source, js, base) ->
+ jsPath = outputPath source, base
+ jsDir = path.dirname jsPath
+ compile = ->
js = ' ' if js.length <= 0
- fs.writeFile jsPath, js, (err) ->
+ fs.writeFile jsPath, js, (err) ->
if err
printLine err.message
else if opts.compile and opts.watch
- console.log "#{(new Date).toLocaleTimeString()} - compiled #{source}"
- path.exists dir, (exists) ->
- if exists then compile() else exec "mkdir -p #{dir}", compilePipe compiled JS through JSLint (requires a working jsl command), printing
-any errors or warnings that arise.
lint = (file, js) ->
- printIt = (buffer) -> printLine file + ':\t' + buffer.toString().trim()
- conf = __dirname + '/../extras/jsl.conf'
+ timeLog "compiled #{source}"
+ path.exists jsDir, (exists) ->
+ if exists then compile() else exec "mkdir -p #{jsDir}", compile
+ Convenience for cleaner setTimeouts.
wait = (milliseconds, func) -> setTimeout func, millisecondsWhen watching scripts, it's useful to log changes with the timestamp.
timeLog = (message) ->
+ console.log "#{(new Date).toLocaleTimeString()} - #{message}"Pipe compiled JS through JSLint (requires a working jsl command), printing
+any errors or warnings that arise.
lint = (file, js) ->
+ printIt = (buffer) -> printLine file + ':\t' + buffer.toString().trim()
+ conf = __dirname + '/../../extras/jsl.conf'
jsl = spawn 'jsl', ['-nologo', '-stdin', '-conf', conf]
jsl.stdout.on 'data', printIt
jsl.stderr.on 'data', printIt
jsl.stdin.write js
- jsl.stdin.end()Pretty-print a stream of tokens.
printTokens = (tokens) ->
+ jsl.stdin.end()Pretty-print a stream of tokens.
printTokens = (tokens) ->
strings = for token in tokens
[tag, value] = [token[0], token[1].toString().replace(/\n/, '\\n')]
"[#{tag} #{value}]"
- printLine strings.join(' ')Use the OptionParser module to extract all options from + printLine strings.join(' ')
Use the OptionParser module to extract all options from
process.argv that are specified in SWITCHES.
parseOptions = ->
optionParser = new optparse.OptionParser SWITCHES, BANNER
o = opts = optionParser.parse process.argv.slice 2
o.compile or= !!o.output
o.run = not (o.compile or o.print or o.lint)
o.print = !! (o.print or (o.eval or o.stdio and o.compile))
- sources = o.argumentsThe compile-time options to pass to the CoffeeScript compiler.
compileOptions = (filename) -> {filename, bare: opts.bare}Start up a new Node.js instance with the arguments in --nodejs passed to
+ sources = o.arguments
+ sourceCode[i] = null for source, i in sources
+ return
The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (filename) -> {filename, bare: opts.bare}Start up a new Node.js instance with the arguments in --nodejs passed to
the node binary, preserving the other options.
forkNode = ->
nodeArgs = opts.nodejs.split /\s+/
args = process.argv[1..]
args.splice args.indexOf('--nodejs'), 2
spawn process.execPath, nodeArgs.concat(args),
- cwd: process.cwd()
- env: process.env
- customFds: [0, 1, 2]Print the --help usage message and exit. Deprecated switches are not
+ cwd: process.cwd()
+ env: process.env
+ customFds: [0, 1, 2]
Print the --help usage message and exit. Deprecated switches are not
shown.
usage = ->
- printLine (new optparse.OptionParser SWITCHES, BANNER).help()Print the --version message and exit.
version = ->
+ printLine (new optparse.OptionParser SWITCHES, BANNER).help()Print the --version message and exit.
version = ->
printLine "CoffeeScript version #{CoffeeScript.VERSION}"