// Copyright 2012 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 fs = require('fs'); var path = require('path'); var Promise = require('rsvp').Promise; var nodeLoader = require('./nodeLoader.js'); var util = require('./file-util.js'); var normalizePath = util.normalizePath; var mkdirRecursive = util.mkdirRecursive; var NodeCompiler = require('./NodeCompiler.js').NodeCompiler;

var cwd = process.cwd();

function recursiveModuleCompileToSingleFile(outputFile, includes, options) {

return new Promise(function (resolve, reject) {
  var resolvedOutputFile = path.resolve(outputFile);
  var outputDir = path.dirname(resolvedOutputFile);

  // Resolve includes before changing directory.
  var resolvedIncludes = includes.map(function(include) {
    include.name = path.resolve(include.name);
    return include;
  });

  var compiler = new NodeCompiler(options);

  mkdirRecursive(outputDir);
  process.chdir(outputDir);

  // Make includes relative to output dir so that sourcemap paths are correct.
  resolvedIncludes = resolvedIncludes.map(function(include) {
    include.name = normalizePath(path.relative(outputDir, include.name));
    return include;
  });

  recursiveModuleCompile(resolvedIncludes, options, function(tree) {
    compiler.writeTreeToFile(tree, resolvedOutputFile);
    process.chdir(cwd);
    resolve();
  }, reject);
});

}

function forEachRecursiveModuleCompile(outputDir, includes, options) {

var outputDir = path.resolve(outputDir);
var compiler = new NodeCompiler(options);

var current = 0;

function next() {
  if (current === includes.length)
    process.exit(0);

  recursiveModuleCompile(includes.slice(current, current + 1), options,
      function(tree) {
        var outputFileName = path.join(outputDir, includes[current].name);
        compiler.writeTreeToFile(tree, outputFileName);
        current++;
        next();
      },
      function(err) {
        process.exit(1);
      });
}

next();

}

var TraceurLoader = traceur.runtime.TraceurLoader; var InlineLoaderCompiler = traceur.runtime.InlineLoaderCompiler; var Options = traceur.util.Options;

/**

* Compiles the files in "fileNamesAndTypes" along with any associated modules,
* into a single js file, in module dependency order.
* TODO: Make this function also use a promise
*
* @param {Array.<Object>} fileNamesAndTypes The list of {name, type}
*     to compile and concat; type is 'module' or 'script'
* @param {Object} options A container for misc options. 'depTarget' is the
*     only currently available option, which results in the dependencies for
*     'fileNamesAndTypes' being printed to stdout, with 'depTarget' as the target.
* @param {Function} callback Callback used to return the result. A null result
*     indicates that recursiveModuleCompile has returned successfully from a
*     non-compile request.
* @param {Function} errback Callback used to return errors.
*/

function recursiveModuleCompile(fileNamesAndTypes, options, callback, errback) {

var depTarget = options && options.depTarget;
var referrerName = options && options.referrer;

var basePath = path.resolve('./') + '/';
basePath = basePath.replace(/\\/g, '/');

var loadCount = 0;
var elements = [];
var loaderCompiler = new InlineLoaderCompiler(elements);

var loader = new TraceurLoader(nodeLoader, basePath, loaderCompiler);

function appendEvaluateModule(name, referrerName) {
  var normalizedName =
      traceur.ModuleStore.normalize(name, referrerName);
  // Create tree for System.get('normalizedName');
  var moduleModule = traceur.codegeneration.module;
  var tree = moduleModule.createModuleEvaluationStatement(normalizedName);
  elements.push(tree);
}

function loadNext() {
  var doEvaluateModule = false;
  var loadFunction = loader.import;
  var input = fileNamesAndTypes[loadCount];
  var name = input.name;

  var optionsCopy = new Options(options); // Give each load a copy of options.

  if (input.type === 'script') {
    loadFunction = loader.loadAsScript;
  } else {
    name = name.replace(/\.js$/,'');
    if (input.format === 'inline')
      optionsCopy.modules = 'inline';
    else if (optionsCopy.modules === 'register')
      doEvaluateModule = true;
  }
  var loadOptions = {
    referrerName: referrerName,
    metadata: {traceurOptions: optionsCopy}
  };
  var codeUnit = loadFunction.call(loader, name, loadOptions).then(
      function() {
        if (doEvaluateModule)
          appendEvaluateModule(name, referrerName);

        loadCount++;
        if (loadCount < fileNamesAndTypes.length) {
          loadNext();
        } else if (depTarget) {
          callback(null);
        } else {
          var tree = loaderCompiler.toTree(basePath, elements);
          callback(tree);
        }
      }, function(err) {
        errback(err);
      }).catch(function(ex) {
        console.error('Internal error ' + (ex.stack || ex));
      });
}

loadNext();

} exports.recursiveModuleCompile = recursiveModuleCompile; exports.recursiveModuleCompileToSingleFile = recursiveModuleCompileToSingleFile; exports.forEachRecursiveModuleCompile = forEachRecursiveModuleCompile;