mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
More work on watching / joining ... Fixes #1941, fixes #365. Watched files may now be added and removed, including with --join
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
(function() {
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compileScript, compileScripts, compileStdio, exec, forkNode, fs, helpers, lint, loadRequires, optionParser, optparse, opts, parseOptions, path, printLine, printTokens, printWarn, sourceCode, sources, spawn, usage, version, watch, writeJs, _ref;
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, forkNode, fs, helpers, lint, loadRequires, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, usage, version, watch, watchDir, writeJs, _ref;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
@@ -35,9 +35,12 @@
|
||||
|
||||
sourceCode = [];
|
||||
|
||||
notSources = {};
|
||||
|
||||
optionParser = null;
|
||||
|
||||
exports.run = function() {
|
||||
var source, _i, _len, _results;
|
||||
parseOptions();
|
||||
if (opts.nodejs) return forkNode();
|
||||
if (opts.help) return usage();
|
||||
@@ -54,66 +57,60 @@
|
||||
process.argv = process.argv.slice(0, 2).concat(opts.literals);
|
||||
process.argv[0] = 'coffee';
|
||||
process.execPath = require.main.filename;
|
||||
return compileScripts();
|
||||
};
|
||||
|
||||
compileScripts = function() {
|
||||
var base, compile, index, source, _len, _results;
|
||||
_results = [];
|
||||
for (index = 0, _len = sources.length; index < _len; index++) {
|
||||
source = sources[index];
|
||||
sourceCode[index] = null;
|
||||
base = path.join(source);
|
||||
compile = function(source, topLevel) {
|
||||
return path.exists(source, function(exists) {
|
||||
if (topLevel && !exists && source.slice(-7) !== '.coffee') {
|
||||
source = sources[sources.indexOf(source)] = "" + source + ".coffee";
|
||||
return compile(source, topLevel);
|
||||
}
|
||||
if (topLevel && !exists) {
|
||||
console.error("File not found: " + source);
|
||||
process.exit(1);
|
||||
}
|
||||
return fs.stat(source, function(err, stats) {
|
||||
if (err) throw err;
|
||||
if (stats.isDirectory()) {
|
||||
return fs.readdir(source, function(err, files) {
|
||||
var file, _i, _len2, _ref2, _results2;
|
||||
if (err) throw err;
|
||||
files = files.map(function(file) {
|
||||
return path.join(source, file);
|
||||
});
|
||||
index = sources.indexOf(source);
|
||||
[].splice.apply(sources, [index, index - index + 1].concat(files)), files;
|
||||
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() {
|
||||
return null;
|
||||
}))), _ref2;
|
||||
_results2 = [];
|
||||
for (_i = 0, _len2 = files.length; _i < _len2; _i++) {
|
||||
file = files[_i];
|
||||
_results2.push(compile(file));
|
||||
}
|
||||
return _results2;
|
||||
});
|
||||
} else if (topLevel || path.extname(source) === '.coffee') {
|
||||
fs.readFile(source, function(err, code) {
|
||||
if (err) throw err;
|
||||
return compileScript(source, code.toString(), base);
|
||||
});
|
||||
if (opts.watch) return watch(source, base);
|
||||
} else {
|
||||
index = sources.indexOf(source);
|
||||
sources.splice(index, 1);
|
||||
return sourceCode.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
_results.push(compile(source, true));
|
||||
for (_i = 0, _len = sources.length; _i < _len; _i++) {
|
||||
source = sources[_i];
|
||||
_results.push(compilePath(source, true, path.join(source)));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
compilePath = function(source, topLevel, base) {
|
||||
return path.exists(source, function(exists) {
|
||||
if (topLevel && !exists && source.slice(-7) !== '.coffee') {
|
||||
source = sources[sources.indexOf(source)] = "" + source + ".coffee";
|
||||
return compilePath(source, topLevel, base);
|
||||
}
|
||||
if (topLevel && !exists) {
|
||||
console.error("File not found: " + source);
|
||||
process.exit(1);
|
||||
}
|
||||
return fs.stat(source, function(err, stats) {
|
||||
if (err) throw err;
|
||||
if (stats.isDirectory()) {
|
||||
if (opts.watch) watchDir(source, base);
|
||||
return fs.readdir(source, function(err, files) {
|
||||
var file, index, _i, _len, _ref2, _results;
|
||||
if (err) throw err;
|
||||
files = files.map(function(file) {
|
||||
return path.join(source, file);
|
||||
});
|
||||
index = sources.indexOf(source);
|
||||
[].splice.apply(sources, [index, index - index + 1].concat(files)), files;
|
||||
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() {
|
||||
return null;
|
||||
}))), _ref2;
|
||||
_results = [];
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
file = files[_i];
|
||||
_results.push(compilePath(file, false, base));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
} else if (topLevel || path.extname(source) === '.coffee') {
|
||||
if (opts.watch) watch(source, base);
|
||||
return fs.readFile(source, function(err, code) {
|
||||
if (err) throw err;
|
||||
return compileScript(source, code.toString(), base);
|
||||
});
|
||||
} else {
|
||||
notSources[source] = true;
|
||||
return removeSource(source);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
compileScript = function(file, input, base) {
|
||||
var o, options, t, task;
|
||||
o = opts;
|
||||
@@ -131,8 +128,9 @@
|
||||
return printLine(CoffeeScript.nodes(t.input).toString().trim());
|
||||
} else if (o.run) {
|
||||
return CoffeeScript.run(t.input, t.options);
|
||||
} else if (o.join && file !== o.join) {
|
||||
return compileJoin(t.file, t.input);
|
||||
} else if (o.join && t.file !== o.join) {
|
||||
sourceCode[sources.indexOf(t.file)] = t.input;
|
||||
return compileJoin();
|
||||
} else {
|
||||
t.output = CoffeeScript.compile(t.input, t.options);
|
||||
CoffeeScript.emit('success', task);
|
||||
@@ -165,8 +163,7 @@
|
||||
});
|
||||
};
|
||||
|
||||
compileJoin = function(file, code) {
|
||||
sourceCode[sources.indexOf(file)] = code;
|
||||
compileJoin = function() {
|
||||
if (!sourceCode.some(function(code) {
|
||||
return code === null;
|
||||
})) {
|
||||
@@ -208,39 +205,92 @@
|
||||
} else if (event === 'rename') {
|
||||
watcher.close();
|
||||
return setTimeout(function() {
|
||||
compile();
|
||||
return watcher = fs.watch(source, callback);
|
||||
return path.exists(source, function(exists) {
|
||||
if (exists) {
|
||||
compile();
|
||||
return watcher = fs.watch(source, callback);
|
||||
} else {
|
||||
removeSource(source);
|
||||
if (opts.join) {
|
||||
compileJoin();
|
||||
} else {
|
||||
fs.unlink(outputPath(source, base), function(err) {
|
||||
if (err) throw err;
|
||||
});
|
||||
}
|
||||
return timeLog("removed " + source);
|
||||
}
|
||||
});
|
||||
}, 250);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
writeJs = function(source, js, base) {
|
||||
var baseDir, compile, dir, filename, jsPath, srcDir;
|
||||
watchDir = function(source, base) {
|
||||
var watcher;
|
||||
return watcher = fs.watch(source, function() {
|
||||
return fs.readdir(source, function(err, files) {
|
||||
var file, _i, _len, _results;
|
||||
if (err) throw err;
|
||||
files = files.map(function(file) {
|
||||
return path.join(source, file);
|
||||
});
|
||||
_results = [];
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
file = files[_i];
|
||||
if (!(!notSources[file] && sources.indexOf(file) < 0)) continue;
|
||||
sources.push(file);
|
||||
sourceCode.push(null);
|
||||
_results.push(compilePath(file, false, base));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
removeSource = function(source) {
|
||||
var index;
|
||||
index = sources.indexOf(source);
|
||||
sources.splice(index, 1);
|
||||
return sourceCode.splice(index, 1);
|
||||
};
|
||||
|
||||
outputPath = function(source, base) {
|
||||
var baseDir, dir, filename, srcDir;
|
||||
filename = path.basename(source, path.extname(source)) + '.js';
|
||||
srcDir = path.dirname(source);
|
||||
baseDir = base === '.' ? srcDir : srcDir.substring(base.length);
|
||||
dir = opts.output ? path.join(opts.output, baseDir) : srcDir;
|
||||
jsPath = path.join(dir, filename);
|
||||
return path.join(dir, filename);
|
||||
};
|
||||
|
||||
writeJs = function(source, js, base) {
|
||||
var compile, jsDir, jsPath;
|
||||
jsPath = outputPath(source, base);
|
||||
jsDir = path.dirname(jsPath);
|
||||
compile = function() {
|
||||
if (js.length <= 0) js = ' ';
|
||||
return fs.writeFile(jsPath, js, function(err) {
|
||||
if (err) {
|
||||
return printLine(err.message);
|
||||
} else if (opts.compile && opts.watch) {
|
||||
return console.log("" + ((new Date).toLocaleTimeString()) + " - compiled " + source);
|
||||
return timeLog("compiled " + source);
|
||||
}
|
||||
});
|
||||
};
|
||||
return path.exists(dir, function(exists) {
|
||||
return path.exists(jsDir, function(exists) {
|
||||
if (exists) {
|
||||
return compile();
|
||||
} else {
|
||||
return exec("mkdir -p " + dir, compile);
|
||||
return exec("mkdir -p " + jsDir, compile);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
timeLog = function(message) {
|
||||
return console.log("" + ((new Date).toLocaleTimeString()) + " - " + message);
|
||||
};
|
||||
|
||||
lint = function(file, js) {
|
||||
var conf, jsl, printIt;
|
||||
printIt = function(buffer) {
|
||||
@@ -270,13 +320,17 @@
|
||||
};
|
||||
|
||||
parseOptions = function() {
|
||||
var o;
|
||||
var i, o, source, _len;
|
||||
optionParser = new optparse.OptionParser(SWITCHES, BANNER);
|
||||
o = opts = optionParser.parse(process.argv.slice(2));
|
||||
o.compile || (o.compile = !!o.output);
|
||||
o.run = !(o.compile || o.print || o.lint);
|
||||
o.print = !!(o.print || (o.eval || o.stdio && o.compile));
|
||||
return sources = o.arguments;
|
||||
sources = o.arguments;
|
||||
for (i = 0, _len = sources.length; i < _len; i++) {
|
||||
source = sources[i];
|
||||
sourceCode[i] = null;
|
||||
}
|
||||
};
|
||||
|
||||
compileOptions = function(filename) {
|
||||
|
||||
@@ -50,6 +50,7 @@ SWITCHES = [
|
||||
opts = {}
|
||||
sources = []
|
||||
sourceCode = []
|
||||
notSources = {}
|
||||
optionParser = null
|
||||
|
||||
# Run `coffee` by parsing passed options and determining what action to take.
|
||||
@@ -72,47 +73,40 @@ exports.run = ->
|
||||
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 = ->
|
||||
|
||||
for source, index in sources
|
||||
sourceCode[index] = null
|
||||
for source in sources
|
||||
compilePath source, yes, path.join source
|
||||
|
||||
base = path.join source
|
||||
|
||||
compile = (source, topLevel) ->
|
||||
path.exists source, (exists) ->
|
||||
if topLevel and not exists and source[-7..] isnt '.coffee'
|
||||
source = sources[sources.indexOf(source)] = "#{source}.coffee"
|
||||
return compile source, topLevel
|
||||
if topLevel and not exists
|
||||
console.error "File not found: #{source}"
|
||||
process.exit 1
|
||||
fs.stat source, (err, stats) ->
|
||||
# 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) ->
|
||||
path.exists source, (exists) ->
|
||||
if topLevel and not exists and source[-7..] isnt '.coffee'
|
||||
source = sources[sources.indexOf(source)] = "#{source}.coffee"
|
||||
return compilePath source, topLevel, base
|
||||
if topLevel and not exists
|
||||
console.error "File not found: #{source}"
|
||||
process.exit 1
|
||||
fs.stat source, (err, stats) ->
|
||||
throw err if err
|
||||
if stats.isDirectory()
|
||||
watchDir source, base if opts.watch
|
||||
fs.readdir source, (err, files) ->
|
||||
throw err if err
|
||||
if stats.isDirectory()
|
||||
fs.readdir source, (err, files) ->
|
||||
throw err if err
|
||||
files = files.map (file) -> path.join source, file
|
||||
index = sources.indexOf source
|
||||
sources[index..index] = files
|
||||
sourceCode[index..index] = files.map -> null
|
||||
compile file for file in files
|
||||
else if topLevel or path.extname(source) is '.coffee'
|
||||
fs.readFile source, (err, code) ->
|
||||
throw err if err
|
||||
compileScript(source, code.toString(), base)
|
||||
watch source, base if opts.watch
|
||||
else
|
||||
index = sources.indexOf source
|
||||
sources.splice index, 1
|
||||
sourceCode.splice index, 1
|
||||
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
|
||||
compileScript(source, code.toString(), base)
|
||||
else
|
||||
notSources[source] = yes
|
||||
removeSource source
|
||||
|
||||
compile source, true
|
||||
|
||||
# Compile a single source script, containing the given code, according to the
|
||||
# requested options. If evaluating the script directly sets `__filename`,
|
||||
@@ -126,8 +120,9 @@ compileScript = (file, input, base) ->
|
||||
if 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 file isnt o.join
|
||||
compileJoin t.file, t.input
|
||||
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
|
||||
@@ -153,8 +148,7 @@ compileStdio = ->
|
||||
|
||||
# If all of the source files are done being read, concatenate and compile
|
||||
# them together.
|
||||
compileJoin = (file, code) ->
|
||||
sourceCode[sources.indexOf(file)] = code
|
||||
compileJoin = ->
|
||||
unless sourceCode.some((code) -> code is null)
|
||||
compileScript opts.join, sourceCode.join('\n'), opts.join
|
||||
|
||||
@@ -188,28 +182,64 @@ watch = (source, base) ->
|
||||
else if event is 'rename'
|
||||
watcher.close()
|
||||
setTimeout ->
|
||||
compile()
|
||||
watcher = fs.watch source, callback
|
||||
path.exists source, (exists) ->
|
||||
if exists
|
||||
compile()
|
||||
watcher = fs.watch source, callback
|
||||
else
|
||||
removeSource source
|
||||
if opts.join
|
||||
compileJoin()
|
||||
else
|
||||
fs.unlink outputPath(source, base), (err) ->
|
||||
throw err if err
|
||||
timeLog "removed #{source}"
|
||||
, 250
|
||||
|
||||
# Watch a directory of files for new additions.
|
||||
watchDir = (source, base) ->
|
||||
watcher = fs.watch source, ->
|
||||
fs.readdir source, (err, files) ->
|
||||
throw err if err
|
||||
files = files.map (file) -> path.join source, file
|
||||
for file in files when not notSources[file] and sources.indexOf(file) < 0
|
||||
sources.push file
|
||||
sourceCode.push null
|
||||
compilePath file, no, base
|
||||
|
||||
# Remove a file from our source list, and source code cache.
|
||||
removeSource = (source) ->
|
||||
index = sources.indexOf source
|
||||
sources.splice index, 1
|
||||
sourceCode.splice index, 1
|
||||
|
||||
# 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
|
||||
path.join dir, filename
|
||||
|
||||
# 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) ->
|
||||
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 = ->
|
||||
jsPath = outputPath source, base
|
||||
jsDir = path.dirname jsPath
|
||||
compile = ->
|
||||
js = ' ' if js.length <= 0
|
||||
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}", compile
|
||||
timeLog "compiled #{source}"
|
||||
path.exists jsDir, (exists) ->
|
||||
if exists then compile() else exec "mkdir -p #{jsDir}", compile
|
||||
|
||||
# When 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.
|
||||
@@ -238,6 +268,8 @@ parseOptions = ->
|
||||
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.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}
|
||||
|
||||
Reference in New Issue
Block a user