text: // ng-annotate.js
  // MIT licensed, see LICENSE file
  // Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com>
  
  "use strict";
  
  var t0 = Date.now();
  var fs = require("fs");
  var os = require("os");
  var fmt = require("simple-fmt");
  var tryor = require("tryor");
  var ngAnnotate = require("./ng-annotate-main");
  var version = "0.13.0";
  var convertSourceMap = require("convert-source-map");
  var optimist = require("optimist")
      .usage("ng-annotate v" + version + "\n\nUsage: ng-annotate OPTIONS <file>\n\n" +
          "provide - instead of <file> to read from stdin\n" +
          "use -a and -r together to remove and add (rebuild) annotations in one go")
      .options("a", {
          alias: "add",
          boolean: true,
          describe: "add dependency injection annotations where non-existing",
      })
      .options("r", {
          alias: "remove",
          boolean: true,
          describe: "remove all existing dependency injection annotations",
      })
      .options("o", {
          describe: "write output to <file>. output is written to stdout by default",
      })
      .options("es6", {
          boolean: true,
          describe: "support ES6 via the Acorn parser (experimental)",
      })
      .options("sourcemap", {
          boolean: true,
          describe: "generate an inline sourcemap"
      })
      .options("sourceroot", {
          describe: "set the sourceRoot property of the generated sourcemap"
      })
      .options("single_quotes", {
          boolean: true,
          describe: "use single quotes (') instead of double quotes (\")",
      })
      .options("regexp", {
          describe: "detect short form myMod.controller(...) iff myMod matches regexp",
      })
      .options("rename", {
          describe: "rename declarations and annotated references\n" +
              "oldname1 newname1 oldname2 newname2 ...",
          default: ""
      })
      .options("plugin", {
          describe: "use plugin with path (experimental)",
      })
      .options("stats", {
          boolean: true,
          describe: "print statistics on stderr (experimental)",
      });
  
  var argv = optimist.argv;
  
  function exit(msg) {
      if (msg) {
          process.stderr.write(msg);
          process.stderr.write("\n");
      }
      process.exit(-1);
  }
  
  // validate options
  if (argv._.length !== 1) {
      optimist.showHelp();
      exit("error: no input file provided");
  }
  
  if (!argv.add && !argv.remove) {
      optimist.showHelp();
      exit("error: missing option --add and/or --remove");
  }
  
  var filename = argv._.shift();
  
  (filename === "-" ? slurpStdin : slurpFile)(runAnnotate);
  
  
  function slurpStdin(cb) {
      var buf = "";
  
      process.stdin.setEncoding("utf8");
      process.stdin.on("data", function(d) {
          buf += d;
      });
      process.stdin.on("end", function() {
          cb(null, buf);
      });
      process.stdin.resume();
  }
  
  function slurpFile(cb) {
      if (!fs.existsSync(filename)) {
          cb(new Error(fmt('error: file not found {0}', filename)));
      }
  
      fs.readFile(filename, cb);
  }
  
  function insertSourcemap(src, map) {
      return convertSourceMap.removeMapFileComments(src) + os.EOL +
          convertSourceMap.fromJSON(map).toComment();
  }
  
  function runAnnotate(err, src) {
      if (err) {
          exit(err.message);
      }
  
      src = String(src);
  
      var config = tryor(function() {
          return JSON.parse(String(fs.readFileSync("ng-annotate-config.json")));
      }, {});
  
      if (filename !== "-") {
          config.inFile = filename;
      }
  
      ["add", "remove", "o", "sourcemap", "sourceroot", "regexp", "rename", "single_quotes", "plugin", "stats", "es6"].forEach(function(opt) {
          if (opt in argv) {
              config[opt] = argv[opt];
          }
      });
  
      if (config.plugin) {
          if (!Array.isArray(config.plugin)) {
              config.plugin = [config.plugin];
          }
          config.plugin = config.plugin.map(function(path) {
              var absPath = tryor(fs.realpathSync.bind(fs, path), null);
              if (!absPath) {
                  exit(fmt('error: plugin file not found {0}', path));
              }
              // the require below may throw an exception on parse-error
              try {
                  return require(absPath);
              } catch (e) {
                  // node will already print file:line and offending line to stderr
                  exit(fmt("error: couldn't require(\"{0}\")", absPath));
              }
          });
      }
  
      var trimmedRename = config.rename && config.rename.trim();
      if (trimmedRename) {
          var flattenRename = trimmedRename.split(" ");
          var renameArray = [];
          for (var i = 0; i < flattenRename.length; i = i + 2) {
              renameArray.push({
                  "from": flattenRename[i],
                  "to": flattenRename[i + 1],
              });
          }
          config.rename = renameArray;
      } else {
          config.rename = null;
      }
  
      var run_t0 = Date.now();
      var ret = ngAnnotate(src, config);
      var run_t1 = Date.now();
  
      if (ret.errors) {
          exit(ret.errors.join("\n"));
      }
  
      var stats = ret._stats;
      if (config.stats && stats) {
          var t1 = Date.now();
          var all = t1 - t0;
          var run_parser = stats.parser_parse_t1 - stats.parser_parse_t0;
          var all_parser = run_parser + (stats.parser_require_t1 - stats.parser_require_t0);
          var nga_run = (run_t1 - run_t0) - run_parser;
          var nga_init = all - all_parser - nga_run;
  
          var pct = function(n) {
              return Math.round(100 * n / all);
          }
  
          process.stderr.write(fmt("[{0} ms] parser: {1}, nga init: {2}, nga run: {3}\n", all, all_parser, nga_init, nga_run));
          process.stderr.write(fmt("[%] parser: {0}, nga init: {1}, nga run: {2}\n", pct(all_parser), pct(nga_init), pct(nga_run)));
      }
  
      var output = config.sourcemap ?
         insertSourcemap(ret.src, ret.map) :
         ret.src;
  
      if (ret.src && config.o) {
          try {
              fs.writeFileSync(config.o, output);
          } catch (e) {
              exit(e.message);
          }
      } else if (ret.src) {
          process.stdout.write(output);
      }
  }
  