PageRenderTime 63ms CodeModel.GetById 24ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/coffee-script/command.js

http://github.com/jashkenas/coffee-script
JavaScript | 610 lines | 564 code | 45 blank | 1 comment | 123 complexity | f8fa20382fe0428a7be928011eaa5a1b MD5 | raw file
  1// Generated by CoffeeScript 1.10.0
  2(function() {
  3  var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, ref, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs,
  4    indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
  5
  6  fs = require('fs');
  7
  8  path = require('path');
  9
 10  helpers = require('./helpers');
 11
 12  optparse = require('./optparse');
 13
 14  CoffeeScript = require('./coffee-script');
 15
 16  ref = require('child_process'), spawn = ref.spawn, exec = ref.exec;
 17
 18  EventEmitter = require('events').EventEmitter;
 19
 20  useWinPathSep = path.sep === '\\';
 21
 22  helpers.extend(CoffeeScript, new EventEmitter);
 23
 24  printLine = function(line) {
 25    return process.stdout.write(line + '\n');
 26  };
 27
 28  printWarn = function(line) {
 29    return process.stderr.write(line + '\n');
 30  };
 31
 32  hidden = function(file) {
 33    return /^\.|~$/.test(file);
 34  };
 35
 36  BANNER = 'Usage: coffee [options] path/to/script.coffee -- [args]\n\nIf called without options, `coffee` will run your script.';
 37
 38  SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-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'], ['-m', '--map', 'generate source map and save as .js.map files'], ['-M', '--inline-map', 'generate source map and include it directly in output'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['--no-header', 'suppress the "Generated by" header'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-r', '--require [MODULE*]', 'require the given module before eval or REPL'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-l', '--literate', 'treat stdio as literate style coffee-script'], ['-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']];
 39
 40  opts = {};
 41
 42  sources = [];
 43
 44  sourceCode = [];
 45
 46  notSources = {};
 47
 48  watchedDirs = {};
 49
 50  optionParser = null;
 51
 52  exports.run = function() {
 53    var i, len, literals, ref1, replCliOpts, results, source;
 54    parseOptions();
 55    replCliOpts = {
 56      useGlobal: true
 57    };
 58    if (opts.require) {
 59      opts.prelude = makePrelude(opts.require);
 60    }
 61    replCliOpts.prelude = opts.prelude;
 62    if (opts.nodejs) {
 63      return forkNode();
 64    }
 65    if (opts.help) {
 66      return usage();
 67    }
 68    if (opts.version) {
 69      return version();
 70    }
 71    if (opts.interactive) {
 72      return require('./repl').start(replCliOpts);
 73    }
 74    if (opts.stdio) {
 75      return compileStdio();
 76    }
 77    if (opts["eval"]) {
 78      return compileScript(null, opts["arguments"][0]);
 79    }
 80    if (!opts["arguments"].length) {
 81      return require('./repl').start(replCliOpts);
 82    }
 83    literals = opts.run ? opts["arguments"].splice(1) : [];
 84    process.argv = process.argv.slice(0, 2).concat(literals);
 85    process.argv[0] = 'coffee';
 86    if (opts.output) {
 87      opts.output = path.resolve(opts.output);
 88    }
 89    if (opts.join) {
 90      opts.join = path.resolve(opts.join);
 91      console.error('\nThe --join option is deprecated and will be removed in a future version.\n\nIf for some reason it\'s necessary to share local variables between files,\nreplace...\n\n    $ coffee --compile --join bundle.js -- a.coffee b.coffee c.coffee\n\nwith...\n\n    $ cat a.coffee b.coffee c.coffee | coffee --compile --stdio > bundle.js\n');
 92    }
 93    ref1 = opts["arguments"];
 94    results = [];
 95    for (i = 0, len = ref1.length; i < len; i++) {
 96      source = ref1[i];
 97      source = path.resolve(source);
 98      results.push(compilePath(source, true, source));
 99    }
100    return results;
101  };
102
103  makePrelude = function(requires) {
104    return requires.map(function(module) {
105      var _, match, name;
106      if (match = module.match(/^(.*)=(.*)$/)) {
107        _ = match[0], name = match[1], module = match[2];
108      }
109      name || (name = helpers.baseFileName(module, true, useWinPathSep));
110      return name + " = require('" + module + "')";
111    }).join(';');
112  };
113
114  compilePath = function(source, topLevel, base) {
115    var code, err, file, files, i, len, results, stats;
116    if (indexOf.call(sources, source) >= 0 || watchedDirs[source] || !topLevel && (notSources[source] || hidden(source))) {
117      return;
118    }
119    try {
120      stats = fs.statSync(source);
121    } catch (error) {
122      err = error;
123      if (err.code === 'ENOENT') {
124        console.error("File not found: " + source);
125        process.exit(1);
126      }
127      throw err;
128    }
129    if (stats.isDirectory()) {
130      if (path.basename(source) === 'node_modules') {
131        notSources[source] = true;
132        return;
133      }
134      if (opts.run) {
135        compilePath(findDirectoryIndex(source), topLevel, base);
136        return;
137      }
138      if (opts.watch) {
139        watchDir(source, base);
140      }
141      try {
142        files = fs.readdirSync(source);
143      } catch (error) {
144        err = error;
145        if (err.code === 'ENOENT') {
146          return;
147        } else {
148          throw err;
149        }
150      }
151      results = [];
152      for (i = 0, len = files.length; i < len; i++) {
153        file = files[i];
154        results.push(compilePath(path.join(source, file), false, base));
155      }
156      return results;
157    } else if (topLevel || helpers.isCoffee(source)) {
158      sources.push(source);
159      sourceCode.push(null);
160      delete notSources[source];
161      if (opts.watch) {
162        watch(source, base);
163      }
164      try {
165        code = fs.readFileSync(source);
166      } catch (error) {
167        err = error;
168        if (err.code === 'ENOENT') {
169          return;
170        } else {
171          throw err;
172        }
173      }
174      return compileScript(source, code.toString(), base);
175    } else {
176      return notSources[source] = true;
177    }
178  };
179
180  findDirectoryIndex = function(source) {
181    var err, ext, i, index, len, ref1;
182    ref1 = CoffeeScript.FILE_EXTENSIONS;
183    for (i = 0, len = ref1.length; i < len; i++) {
184      ext = ref1[i];
185      index = path.join(source, "index" + ext);
186      try {
187        if ((fs.statSync(index)).isFile()) {
188          return index;
189        }
190      } catch (error) {
191        err = error;
192        if (err.code !== 'ENOENT') {
193          throw err;
194        }
195      }
196    }
197    console.error("Missing index.coffee or index.litcoffee in " + source);
198    return process.exit(1);
199  };
200
201  compileScript = function(file, input, base) {
202    var compiled, err, message, o, options, t, task;
203    if (base == null) {
204      base = null;
205    }
206    o = opts;
207    options = compileOptions(file, base);
208    try {
209      t = task = {
210        file: file,
211        input: input,
212        options: options
213      };
214      CoffeeScript.emit('compile', task);
215      if (o.tokens) {
216        return printTokens(CoffeeScript.tokens(t.input, t.options));
217      } else if (o.nodes) {
218        return printLine(CoffeeScript.nodes(t.input, t.options).toString().trim());
219      } else if (o.run) {
220        CoffeeScript.register();
221        if (opts.prelude) {
222          CoffeeScript["eval"](opts.prelude, t.options);
223        }
224        return CoffeeScript.run(t.input, t.options);
225      } else if (o.join && t.file !== o.join) {
226        if (helpers.isLiterate(file)) {
227          t.input = helpers.invertLiterate(t.input);
228        }
229        sourceCode[sources.indexOf(t.file)] = t.input;
230        return compileJoin();
231      } else {
232        compiled = CoffeeScript.compile(t.input, t.options);
233        t.output = compiled;
234        if (o.map) {
235          t.output = compiled.js;
236          t.sourceMap = compiled.v3SourceMap;
237        }
238        CoffeeScript.emit('success', task);
239        if (o.print) {
240          return printLine(t.output.trim());
241        } else if (o.compile || o.map) {
242          return writeJs(base, t.file, t.output, options.jsPath, t.sourceMap);
243        }
244      }
245    } catch (error) {
246      err = error;
247      CoffeeScript.emit('failure', err, task);
248      if (CoffeeScript.listeners('failure').length) {
249        return;
250      }
251      message = err.stack || ("" + err);
252      if (o.watch) {
253        return printLine(message + '\x07');
254      } else {
255        printWarn(message);
256        return process.exit(1);
257      }
258    }
259  };
260
261  compileStdio = function() {
262    var buffers, stdin;
263    buffers = [];
264    stdin = process.openStdin();
265    stdin.on('data', function(buffer) {
266      if (buffer) {
267        return buffers.push(buffer);
268      }
269    });
270    return stdin.on('end', function() {
271      return compileScript(null, Buffer.concat(buffers).toString());
272    });
273  };
274
275  joinTimeout = null;
276
277  compileJoin = function() {
278    if (!opts.join) {
279      return;
280    }
281    if (!sourceCode.some(function(code) {
282      return code === null;
283    })) {
284      clearTimeout(joinTimeout);
285      return joinTimeout = wait(100, function() {
286        return compileScript(opts.join, sourceCode.join('\n'), opts.join);
287      });
288    }
289  };
290
291  watch = function(source, base) {
292    var compile, compileTimeout, err, prevStats, rewatch, startWatcher, watchErr, watcher;
293    watcher = null;
294    prevStats = null;
295    compileTimeout = null;
296    watchErr = function(err) {
297      if (err.code !== 'ENOENT') {
298        throw err;
299      }
300      if (indexOf.call(sources, source) < 0) {
301        return;
302      }
303      try {
304        rewatch();
305        return compile();
306      } catch (error) {
307        removeSource(source, base);
308        return compileJoin();
309      }
310    };
311    compile = function() {
312      clearTimeout(compileTimeout);
313      return compileTimeout = wait(25, function() {
314        return fs.stat(source, function(err, stats) {
315          if (err) {
316            return watchErr(err);
317          }
318          if (prevStats && stats.size === prevStats.size && stats.mtime.getTime() === prevStats.mtime.getTime()) {
319            return rewatch();
320          }
321          prevStats = stats;
322          return fs.readFile(source, function(err, code) {
323            if (err) {
324              return watchErr(err);
325            }
326            compileScript(source, code.toString(), base);
327            return rewatch();
328          });
329        });
330      });
331    };
332    startWatcher = function() {
333      return watcher = fs.watch(source).on('change', compile).on('error', function(err) {
334        if (err.code !== 'EPERM') {
335          throw err;
336        }
337        return removeSource(source, base);
338      });
339    };
340    rewatch = function() {
341      if (watcher != null) {
342        watcher.close();
343      }
344      return startWatcher();
345    };
346    try {
347      return startWatcher();
348    } catch (error) {
349      err = error;
350      return watchErr(err);
351    }
352  };
353
354  watchDir = function(source, base) {
355    var err, readdirTimeout, startWatcher, stopWatcher, watcher;
356    watcher = null;
357    readdirTimeout = null;
358    startWatcher = function() {
359      return watcher = fs.watch(source).on('error', function(err) {
360        if (err.code !== 'EPERM') {
361          throw err;
362        }
363        return stopWatcher();
364      }).on('change', function() {
365        clearTimeout(readdirTimeout);
366        return readdirTimeout = wait(25, function() {
367          var err, file, files, i, len, results;
368          try {
369            files = fs.readdirSync(source);
370          } catch (error) {
371            err = error;
372            if (err.code !== 'ENOENT') {
373              throw err;
374            }
375            return stopWatcher();
376          }
377          results = [];
378          for (i = 0, len = files.length; i < len; i++) {
379            file = files[i];
380            results.push(compilePath(path.join(source, file), false, base));
381          }
382          return results;
383        });
384      });
385    };
386    stopWatcher = function() {
387      watcher.close();
388      return removeSourceDir(source, base);
389    };
390    watchedDirs[source] = true;
391    try {
392      return startWatcher();
393    } catch (error) {
394      err = error;
395      if (err.code !== 'ENOENT') {
396        throw err;
397      }
398    }
399  };
400
401  removeSourceDir = function(source, base) {
402    var file, i, len, sourcesChanged;
403    delete watchedDirs[source];
404    sourcesChanged = false;
405    for (i = 0, len = sources.length; i < len; i++) {
406      file = sources[i];
407      if (!(source === path.dirname(file))) {
408        continue;
409      }
410      removeSource(file, base);
411      sourcesChanged = true;
412    }
413    if (sourcesChanged) {
414      return compileJoin();
415    }
416  };
417
418  removeSource = function(source, base) {
419    var index;
420    index = sources.indexOf(source);
421    sources.splice(index, 1);
422    sourceCode.splice(index, 1);
423    if (!opts.join) {
424      silentUnlink(outputPath(source, base));
425      silentUnlink(outputPath(source, base, '.js.map'));
426      return timeLog("removed " + source);
427    }
428  };
429
430  silentUnlink = function(path) {
431    var err, ref1;
432    try {
433      return fs.unlinkSync(path);
434    } catch (error) {
435      err = error;
436      if ((ref1 = err.code) !== 'ENOENT' && ref1 !== 'EPERM') {
437        throw err;
438      }
439    }
440  };
441
442  outputPath = function(source, base, extension) {
443    var basename, dir, srcDir;
444    if (extension == null) {
445      extension = ".js";
446    }
447    basename = helpers.baseFileName(source, true, useWinPathSep);
448    srcDir = path.dirname(source);
449    if (!opts.output) {
450      dir = srcDir;
451    } else if (source === base) {
452      dir = opts.output;
453    } else {
454      dir = path.join(opts.output, path.relative(base, srcDir));
455    }
456    return path.join(dir, basename + extension);
457  };
458
459  mkdirp = function(dir, fn) {
460    var mkdirs, mode;
461    mode = 0x1ff & ~process.umask();
462    return (mkdirs = function(p, fn) {
463      return fs.exists(p, function(exists) {
464        if (exists) {
465          return fn();
466        } else {
467          return mkdirs(path.dirname(p), function() {
468            return fs.mkdir(p, mode, function(err) {
469              if (err) {
470                return fn(err);
471              }
472              return fn();
473            });
474          });
475        }
476      });
477    })(dir, fn);
478  };
479
480  writeJs = function(base, sourcePath, js, jsPath, generatedSourceMap) {
481    var compile, jsDir, sourceMapPath;
482    if (generatedSourceMap == null) {
483      generatedSourceMap = null;
484    }
485    sourceMapPath = outputPath(sourcePath, base, ".js.map");
486    jsDir = path.dirname(jsPath);
487    compile = function() {
488      if (opts.compile) {
489        if (js.length <= 0) {
490          js = ' ';
491        }
492        if (generatedSourceMap) {
493          js = js + "\n//# sourceMappingURL=" + (helpers.baseFileName(sourceMapPath, false, useWinPathSep)) + "\n";
494        }
495        fs.writeFile(jsPath, js, function(err) {
496          if (err) {
497            printLine(err.message);
498            return process.exit(1);
499          } else if (opts.compile && opts.watch) {
500            return timeLog("compiled " + sourcePath);
501          }
502        });
503      }
504      if (generatedSourceMap) {
505        return fs.writeFile(sourceMapPath, generatedSourceMap, function(err) {
506          if (err) {
507            printLine("Could not write source map: " + err.message);
508            return process.exit(1);
509          }
510        });
511      }
512    };
513    return fs.exists(jsDir, function(itExists) {
514      if (itExists) {
515        return compile();
516      } else {
517        return mkdirp(jsDir, compile);
518      }
519    });
520  };
521
522  wait = function(milliseconds, func) {
523    return setTimeout(func, milliseconds);
524  };
525
526  timeLog = function(message) {
527    return console.log(((new Date).toLocaleTimeString()) + " - " + message);
528  };
529
530  printTokens = function(tokens) {
531    var strings, tag, token, value;
532    strings = (function() {
533      var i, len, results;
534      results = [];
535      for (i = 0, len = tokens.length; i < len; i++) {
536        token = tokens[i];
537        tag = token[0];
538        value = token[1].toString().replace(/\n/, '\\n');
539        results.push("[" + tag + " " + value + "]");
540      }
541      return results;
542    })();
543    return printLine(strings.join(' '));
544  };
545
546  parseOptions = function() {
547    var o;
548    optionParser = new optparse.OptionParser(SWITCHES, BANNER);
549    o = opts = optionParser.parse(process.argv.slice(2));
550    o.compile || (o.compile = !!o.output);
551    o.run = !(o.compile || o.print || o.map);
552    return o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
553  };
554
555  compileOptions = function(filename, base) {
556    var answer, cwd, jsDir, jsPath;
557    answer = {
558      filename: filename,
559      literate: opts.literate || helpers.isLiterate(filename),
560      bare: opts.bare,
561      header: opts.compile && !opts['no-header'],
562      sourceMap: opts.map,
563      inlineMap: opts['inline-map']
564    };
565    if (filename) {
566      if (base) {
567        cwd = process.cwd();
568        jsPath = outputPath(filename, base);
569        jsDir = path.dirname(jsPath);
570        answer = helpers.merge(answer, {
571          jsPath: jsPath,
572          sourceRoot: path.relative(jsDir, cwd),
573          sourceFiles: [path.relative(cwd, filename)],
574          generatedFile: helpers.baseFileName(jsPath, false, useWinPathSep)
575        });
576      } else {
577        answer = helpers.merge(answer, {
578          sourceRoot: "",
579          sourceFiles: [helpers.baseFileName(filename, false, useWinPathSep)],
580          generatedFile: helpers.baseFileName(filename, true, useWinPathSep) + ".js"
581        });
582      }
583    }
584    return answer;
585  };
586
587  forkNode = function() {
588    var args, nodeArgs, p;
589    nodeArgs = opts.nodejs.split(/\s+/);
590    args = process.argv.slice(1);
591    args.splice(args.indexOf('--nodejs'), 2);
592    p = spawn(process.execPath, nodeArgs.concat(args), {
593      cwd: process.cwd(),
594      env: process.env,
595      stdio: [0, 1, 2]
596    });
597    return p.on('exit', function(code) {
598      return process.exit(code);
599    });
600  };
601
602  usage = function() {
603    return printLine((new optparse.OptionParser(SWITCHES, BANNER)).help());
604  };
605
606  version = function() {
607    return printLine("CoffeeScript version " + CoffeeScript.VERSION);
608  };
609
610}).call(this);