// Copyright 2013 Traceur Authors. // // Licensed under the Apache License, Version 2.0 (the “License”); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an “AS IS” BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License.

'use strict';

var path = require('path'); var flags; var cmdName = path.basename(process.argv); try {

flags = new (require('commander').Command)(cmdName);

} catch (ex) {

console.error('Commander.js is required for this to work. To install it ' +
              'run:\n\n  npm install commander\n');
process.exit(1);

} flags.setMaxListeners(100);

var traceur = require('./traceur.js');

// The System object requires traceur, but we want it set for everything that // follows. The module sets global.System as a side-effect. require('./System.js');

flags.option('–out <FILE>', 'Compile all input files into a single file'); flags.option('–referrer <name>',

'Prefix compiled code with System.referrName');

flags.option('–dir <INDIR> <OUTDIR>', 'Compile an input directory of modules into an output directory');

flags.option('–sourcemap', 'Generate source maps'); flags.on('sourcemap', function() {

flags.sourceMaps = traceur.options.sourceMaps = true;

});

flags.option('–longhelp', 'Show all known options'); flags.on('longhelp', function() {

flags.help();
process.exit();

});

// Caling process.exit when there is still characters to be flushed to stdout // makes Windows drop those characters. We therefor wait until the buffer is // empty before really exiting. // Since this makes exiting async we need to manually keep track var shouldExit = false;

function processExit() {

shouldExit = true;
var draining = 0;
function exit() {
  if (!draining--)
    process.exit();
}
if (process.stdout.bufferSize) {
  draining += 1;
  process.stdout.once('drain', exit);
}
if (process.stderr.bufferSize) {
  draining += 1;
  process.stderr.once('drain', exit);
}
exit();

}

flags.option('-v, –version', 'Show version and exit'); flags.on('version', function() {

process.stdout.write(System.version.split('@')[1]);
processExit();

});

flags.on('–help', function() {

console.log('  Examples:');
console.log('');
console.log('    $ %s a.js [args]', cmdName);
console.log('    $ %s --out compiled.js b.js c.js', cmdName);
console.log('    $ %s --dir indir outdir', cmdName);
console.log('');

});

traceur.options.addOptions(flags);

flags.usage(‘ [files]');

// Override commander.js's optionHelp to filter out the Traceur feature flags // from showing up in the help message. var optionHelp = flags.optionHelp; flags.optionHelp = function() {

if (!flags.longhelp) {
  this.options = this.options.filter(function(command) {
    var dashedName = command.long.slice(2);
    return traceur.options.filterOption(dashedName);
  });
}
return optionHelp.call(this);

}

/**

* HACK: Process arguments so that in interpret mode, commander.js only parses
* the flags, without the file name and anything past that. If an invalid flag
* is encountered, commander.js error reporting is emulated instead.
* @param {Array.<string>} argv
* @return {Array.<string>}
*/

function processArguments(argv) {

// Preserve the original.
argv = argv.slice();

var interpretMode = true;
for (var i = 2; i < argv.length; i++) {
  var arg = argv[i], index;
  if (arg === '--')
    break;

  // Normalize flags in-place.
  if (arg.length > 2 && arg[0] === '-' && arg[1] !== '-') {
    // TODO: Is this needed at all for traceur?
    arg = arg.slice(1).split('').map(function(flag) {
      return '-' + flag;
    });
    // Insert the normalized flags in argv.
    argv.splice.apply(argv, [i, 1].concat(arg));
    // Grab the first normalized flag and process it as usual.
    arg = argv[i];
  } else if (/^--/.test(arg) && (index = arg.indexOf('=')) !== -1) {
    // Insert the flag argument in argv.
    argv.splice(i + 1, 0, arg.slice(index + 1));
    // Replace the flag with the stripped version and process it as usual.
    arg = argv[i] = arg.slice(0, index);
  }

  var option = flags.optionFor(arg);
  if (option) {
    if (arg === '--out' || arg === '--dir')
      interpretMode = false;

    if (option.required)
      i++;
    else if (option.optional) {
      arg = argv[i + 1];
      if (arg && arg[0] !== '-')
        i++;
    }
  } else if (arg === '-h' || arg === '--help') {
    // HACK: Special case for the implicit help flags, which can't have
    // their own option, as --help would set flags.help to true, shadowing
    // the flags.help() method.
  } else if (arg[0] === '-') {
    // HACK: Because commander.js has a flexible policy, this is the only
    // reliable way of reporting invalid flags to the user, and it's limited
    // to the first invalid flag encountered.
    console.log('\n  error: unknown option `%s\'\n', arg);
    process.exit(1);
  } else if (interpretMode) {
    // Add a hint to stop commander.js from parsing following arguments.
    // Note that this means that --out must come before any unknown flag as
    // well as before any filename for it to be used as the out flag.
    argv.splice(i, 0, '--');
    // Save traceur flags for interpret.js.
    argv.flags = argv.slice(2, i);
    break;
  }
}
return argv;

}

var argv = processArguments(process.argv); flags.parse(argv);

var includes = traceur.options.scripts || []; includes = includes.concat(flags.args);

if (!shouldExit && !includes.length) {

// TODO: Start trepl
console.error('\n  Error: At least one input file is needed');
flags.help();
process.exit(1);

}

var interpret = require('./interpreter.js'); var compiler = require('./compiler.js'); var compileToSingleFile = compiler.compileToSingleFile; var compileToDirectory = compiler.compileToDirectory;

var out = flags.out; var dir = flags.dir; if (!shouldExit) {

if (out) {
  var isSingleFileCompile = /\.js$/.test(out);
  if (isSingleFileCompile)
    compileToSingleFile(out, includes, flags.sourceMaps);
  else
    compileToDirectory(out, includes, flags.sourceMaps);
} else if (dir) {
  var compileAllJsFilesInDir = require('./compile-single-file.js').compileAllJsFilesInDir;
  compileAllJsFilesInDir(dir, includes[0], true);
}
else {
  interpret(path.resolve(includes[0]), includes.slice(1), argv.flags);
}

}