text: /**
   * Copyright (c) 2015-present, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   */
  'use strict';
  
  var fs = require('fs');
  var q = require('q');
  var Cache = require('./Cache');
  var _ = require('underscore');
  var workerFarm = require('worker-farm');
  var declareOpts = require('../lib/declareOpts');
  var util = require('util');
  
  var readFile = q.nfbind(fs.readFile);
  
  module.exports = Transformer;
  Transformer.TransformError = TransformError;
  
  var validateOpts = declareOpts({
    projectRoots: {
      type: 'array',
      required: true,
    },
    blacklistRE: {
      type: 'object', // typeof regex is object
    },
    polyfillModuleNames: {
      type: 'array',
      default: [],
    },
    cacheVersion: {
      type: 'string',
      default: '1.0',
    },
    resetCache: {
      type: 'boolean',
      default: false,
    },
    transformModulePath: {
      type:'string',
      required: false,
    },
    nonPersistent: {
      type: 'boolean',
      default: false,
    },
  });
  
  function Transformer(options) {
    var opts = validateOpts(options);
  
    this._cache = opts.nonPersistent
      ? new DummyCache()
      : new Cache({
        resetCache: options.resetCache,
        cacheVersion: options.cacheVersion,
        projectRoots: options.projectRoots,
      });
  
    if (options.transformModulePath == null) {
      this._failedToStart = q.Promise.reject(new Error('No transfrom module'));
    } else {
      this._workers = workerFarm(
        {autoStart: true, maxConcurrentCallsPerWorker: 1},
        options.transformModulePath
      );
    }
  }
  
  Transformer.prototype.kill = function() {
    this._workers && workerFarm.end(this._workers);
    return this._cache.end();
  };
  
  Transformer.prototype.invalidateFile = function(filePath) {
    this._cache.invalidate(filePath);
  };
  
  Transformer.prototype.loadFileAndTransform = function(filePath) {
    if (this._failedToStart) {
      return this._failedToStart;
    }
  
    var workers = this._workers;
    return this._cache.get(filePath, function() {
      return readFile(filePath)
        .then(function(buffer) {
          var sourceCode = buffer.toString();
  
          return q.nfbind(workers)({
            sourceCode: sourceCode,
            filename: filePath,
          }).then(
            function(res) {
              if (res.error) {
                throw formatError(res.error, filePath, sourceCode);
              }
  
              return {
                code: res.code,
                sourcePath: filePath,
                sourceCode: sourceCode
              };
            }
          );
        });
    });
  };
  
  function TransformError() {}
  util.inherits(TransformError, SyntaxError);
  
  function formatError(err, filename, source) {
    if (err.lineNumber && err.column) {
      return formatEsprimaError(err, filename, source);
    } else {
      return formatGenericError(err, filename, source);
    }
  }
  
  function formatGenericError(err, filename) {
    var msg = 'TransformError: ' + filename + ': ' + err.message;
    var error = new TransformError();
    var stack = err.stack.split('\n').slice(0, -1);
    stack.push(msg);
    error.stack = stack.join('\n');
    error.message = msg;
    error.type = 'TransformError';
    return error;
  }
  
  function formatEsprimaError(err, filename, source) {
    var stack = err.stack.split('\n');
    stack.shift();
  
    var msg = 'TransformError: ' + err.description + ' ' +  filename + ':' +
      err.lineNumber + ':' + err.column;
    var sourceLine = source.split('\n')[err.lineNumber - 1];
    var snippet = sourceLine + '\n' + new Array(err.column - 1).join(' ') + '^';
  
    stack.unshift(msg);
  
    var error = new TransformError();
    error.message = msg;
    error.type = 'TransformError';
    error.stack = stack.join('\n');
    error.snippet = snippet;
    error.filename = filename;
    error.lineNumber = err.lineNumber;
    error.column = err.column;
    error.description = err.description;
    return error;
  }
  
  function DummyCache() {}
  DummyCache.prototype.get = function(filePath, loaderCb) {
    return loaderCb();
  };
  DummyCache.prototype.end =
  DummyCache.prototype.invalidate = function(){};
  