"use strict";

var _interopRequireWildcard = function (obj) { return obj && obj.__esModule ? obj : { "default": obj }; };

var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };

require("../../polyfill");

var sourceMapSupport = _interopRequire(require("source-map-support"));

var registerCache = _interopRequireWildcard(require("./cache"));

var resolveRc = _interopRequire(require("../../tools/resolve-rc"));

var extend = _interopRequire(require("lodash/object/extend"));

var babel = _interopRequireWildcard(require("../node"));

var each = _interopRequire(require("lodash/collection/each"));

var util = _interopRequireWildcard(require("../../util"));

var fs = _interopRequire(require("fs"));

var slash = _interopRequire(require("slash"));

sourceMapSupport.install({
  handleUncaughtExceptions: false,
  retrieveSourceMap: function retrieveSourceMap(source) {
    var map = maps && maps[source];
    if (map) {
      return {
        url: null,
        map: map
      };
    } else {
      return null;
    }
  }
});

//

registerCache.load();
var cache = registerCache.get();

//

var transformOpts = {};
var ignoreRegex = /node_modules/;
var onlyRegex;
var oldHandlers = {};
var maps = {};

var mtime = function mtime(filename) {
  return +fs.statSync(filename).mtime;
};

var compile = function compile(filename) {
  var result;

  var opts = extend({}, transformOpts);

  // this will be done when the file is transformed anyway but we need all
  // the options so we can generate the cache key
  resolveRc(filename, opts);

  var cacheKey = "" + filename + ":" + JSON.stringify(opts) + ":" + babel.version;

  if (cache) {
    var cached = cache[cacheKey];
    if (cached && cached.mtime === mtime(filename)) {
      result = cached;
    }
  }

  if (!result) {
    result = babel.transformFileSync(filename, extend(opts, {
      sourceMap: "both",
      ast: false
    }));
  }

  if (cache) {
    result.mtime = mtime(filename);
    cache[cacheKey] = result;
  }

  maps[filename] = result.map;

  return result.code;
};

var shouldIgnore = function shouldIgnore(filename) {
  filename = slash(filename);
  return ignoreRegex && ignoreRegex.test(filename) || onlyRegex && !onlyRegex.test(filename);
};

var istanbulMonkey = {};

if (process.env.running_under_istanbul) {
  // jshint ignore:line
  // we need to monkey patch fs.readFileSync so we can hook into
  // what istanbul gets, it's extremely dirty but it's the only way
  var _readFileSync = fs.readFileSync;

  fs.readFileSync = function (filename) {
    if (istanbulMonkey[filename]) {
      delete istanbulMonkey[filename];
      var code = compile(filename);
      istanbulMonkey[filename] = true;
      return code;
    } else {
      return _readFileSync.apply(this, arguments);
    }
  };
}

var istanbulLoader = function istanbulLoader(m, filename, old) {
  istanbulMonkey[filename] = true;
  old(m, filename);
};

var normalLoader = function normalLoader(m, filename) {
  m._compile(compile(filename), filename);
};

var registerExtension = function registerExtension(ext) {
  var old = oldHandlers[ext] || oldHandlers[".js"];

  var loader = normalLoader;
  if (process.env.running_under_istanbul) loader = istanbulLoader; // jshint ignore:line

  require.extensions[ext] = function (m, filename) {
    if (shouldIgnore(filename)) {
      old(m, filename);
    } else {
      loader(m, filename, old);
    }
  };
};

var hookExtensions = function hookExtensions(_exts) {
  each(oldHandlers, function (old, ext) {
    if (old === undefined) {
      delete require.extensions[ext];
    } else {
      require.extensions[ext] = old;
    }
  });

  oldHandlers = {};

  each(_exts, function (ext) {
    oldHandlers[ext] = require.extensions[ext];
    registerExtension(ext);
  });
};

hookExtensions(util.canCompile.EXTENSIONS);

module.exports = function () {
  var opts = arguments[0] === undefined ? {} : arguments[0];

  if (opts.only != null) onlyRegex = util.regexify(opts.only);
  if (opts.ignore != null) ignoreRegex = util.regexify(opts.ignore);

  if (opts.extensions) hookExtensions(util.arrayify(opts.extensions));

  if (opts.cache === false) cache = null;

  delete opts.extensions;
  delete opts.ignore;
  delete opts.cache;
  delete opts.only;

  extend(transformOpts, opts);
};