"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Bundler = void 0;
var async = require("async");
var fs = require("fs");
var lodash = require("lodash");
var path = require("path");
var tmp = require("tmp");
var benchmark_1 = require("../shared/benchmark");
var PathTool = require("../shared/path-tool");
var bundle_item_1 = require("./bundle-item");
require("../client/commonjs");
var Bundler = /** @class */ (function () {
    function Bundler(config, dependencyWalker, globals, log, project, resolver, sourceMap, transformer, validator) {
        this.config = config;
        this.dependencyWalker = dependencyWalker;
        this.globals = globals;
        this.log = log;
        this.project = project;
        this.resolver = resolver;
        this.sourceMap = sourceMap;
        this.transformer = transformer;
        this.validator = validator;
        this.BUNDLE_DELAY = 500;
        this.bundleQueuedModulesDeferred = lodash.debounce(this.bundleQueuedModules, this.BUNDLE_DELAY);
        this.bundleBuffer = [];
        this.bundleFile = tmp.fileSync({
            postfix: ".js",
            prefix: "karma-typescript-bundle-"
        });
        this.bundleQueue = [];
        this.entrypoints = [];
        this.projectImportCountOnFirstRun = undefined;
    }
    Bundler.prototype.attach = function (files) {
        files.unshift({
            included: true,
            pattern: this.bundleFile.name,
            served: true,
            watched: true
        });
        files.push({
            included: true,
            pattern: path.join(__dirname, "../../dist/client/commonjs.js"),
            served: true,
            watched: false
        });
    };
    Bundler.prototype.bundle = function (file, emitOutput, callback) {
        this.bundleQueue.push({ callback: callback, emitOutput: emitOutput, file: file });
        this.bundleQueuedModulesDeferred();
    };
    Bundler.prototype.bundleQueuedModules = function () {
        var _this = this;
        // Drain the bundle queue and keep a copy of the current items. This prevents problems
        // when a new bundle is queued while transformations are running.
        var queuedItems = this.bundleQueue.slice(0);
        this.bundleQueue.length = 0;
        var benchmark = new benchmark_1.Benchmark();
        this.transformer.applyTsTransforms(queuedItems, function () {
            queuedItems.forEach(function (queued) {
                var source = _this.sourceMap.removeSourceMapComment(queued);
                var map = _this.sourceMap.getSourceMap(queued);
                if (map) {
                    // used by Karma to log errors with original source code line numbers
                    queued.file.sourceMap = map.toObject();
                }
                queued.item = new bundle_item_1.BundleItem(queued.file.path, queued.file.originalPath, source, map);
            });
            var dependencyCount = _this.dependencyWalker.collectTypescriptDependencies(queuedItems);
            if (_this.shouldBundle(dependencyCount)) {
                _this.bundleWithLoader(benchmark, queuedItems);
            }
            else {
                _this.bundleWithoutLoader(queuedItems);
            }
        });
    };
    Bundler.prototype.shouldBundle = function (dependencyCount) {
        if (this.config.hasPreprocessor("commonjs")) {
            this.log.debug("Preprocessor 'commonjs' detected, code will NOT be bundled");
            return false;
        }
        if (!this.project.hasCompatibleModuleKind()) {
            this.log.debug("Module kind set to '%s', code will NOT be bundled", this.project.getModuleKind());
            return false;
        }
        if (this.projectImportCountOnFirstRun === undefined) {
            this.projectImportCountOnFirstRun = dependencyCount;
        }
        this.log.debug("Project has %s import/require statements, code will be%sbundled", this.projectImportCountOnFirstRun, this.projectImportCountOnFirstRun > 0 ? " " : " NOT ");
        return this.projectImportCountOnFirstRun > 0;
    };
    Bundler.prototype.bundleWithLoader = function (benchmark, queuedItems) {
        var _this = this;
        async.each(queuedItems, function (queued, onQueuedResolved) {
            _this.addEntrypointFilename(queued.item.filename);
            async.each(queued.item.dependencies, function (bundleItem, onDependencyResolved) {
                _this.resolver.resolveModule(queued.item.moduleName, bundleItem, _this.bundleBuffer, function () {
                    onDependencyResolved();
                });
            }, onQueuedResolved);
        }, function () {
            _this.onAllResolved(benchmark, queuedItems);
        });
    };
    Bundler.prototype.bundleWithoutLoader = function (queuedItems) {
        var _this = this;
        this.globals.add(this.bundleBuffer, this.entrypoints, function () {
            _this.writeMainBundleFile(function () {
                queuedItems.forEach(function (queued) {
                    var source = queued.item.source + "\n" +
                        (queued.item.sourceMap ? queued.item.sourceMap.toComment() + "\n" : "");
                    queued.callback(source);
                });
            });
        });
    };
    Bundler.prototype.onAllResolved = function (benchmark, queuedItems) {
        var _this = this;
        this.orderEntrypoints();
        this.globals.add(this.bundleBuffer, this.entrypoints, function () {
            _this.writeMainBundleFile(function () {
                _this.log.info("Bundled imports for %s file(s) in %s ms.", queuedItems.length, benchmark.elapsed());
                queuedItems.forEach(function (queued) {
                    queued.callback(_this.addLoaderFunction(queued.item, true));
                });
                _this.log.debug("Karma callbacks for %s file(s) in %s ms.", queuedItems.length, benchmark.elapsed());
            });
        });
    };
    Bundler.prototype.addLoaderFunction = function (bundleItem, standalone) {
        var _this = this;
        var dependencyMap = {};
        var moduleId = path.relative(this.config.karma.basePath, bundleItem.filename);
        bundleItem.dependencies.forEach(function (dependency) {
            if (!dependency.filename) {
                _this.log.debug("No resolved filename for module [%s], required by [%s]", dependency.moduleName, bundleItem.filename);
            }
            else {
                dependencyMap[dependency.moduleName] = PathTool.fixWindowsPath(dependency.filename);
            }
        });
        return (standalone ? "(function(global){" : "") +
            "global.wrappers['" + PathTool.fixWindowsPath(bundleItem.filename) + "']=" +
            "[function(require,module,exports,__dirname,__filename){ " + bundleItem.source +
            "\n},'" +
            PathTool.fixWindowsPath(moduleId) + "'," +
            PathTool.fixWindowsPath(JSON.stringify(dependencyMap)) + "];" +
            (standalone ? "})(this);" : "") + "\n" +
            (bundleItem.sourceMap ? bundleItem.sourceMap.toComment() + "\n" : "");
    };
    Bundler.prototype.createEntrypointFilenames = function () {
        if (this.entrypoints.length > 0) {
            return "global.entrypointFilenames=['" + this.entrypoints.join("','") + "'];\n";
        }
        return "";
    };
    Bundler.prototype.addEntrypointFilename = function (filename) {
        if (this.config.bundlerOptions.entrypoints.test(filename) &&
            this.entrypoints.indexOf(filename) === -1) {
            this.entrypoints.push(filename);
        }
    };
    Bundler.prototype.orderEntrypoints = function () {
        var _this = this;
        var orderedEntrypoints = [];
        this.project.getKarmaFiles().forEach(function (filename) {
            if (_this.entrypoints.indexOf(filename) !== -1) {
                orderedEntrypoints.push(filename);
            }
        });
        this.entrypoints = orderedEntrypoints;
    };
    Bundler.prototype.writeMainBundleFile = function (onMainBundleFileWritten) {
        var _this = this;
        var bundle = "(function(global){\nglobal.wrappers={};\n";
        this.sourceMap.initialize(bundle);
        this.bundleBuffer.forEach(function (bundleItem) {
            _this.sourceMap.addFile(bundleItem);
            var wrapped = _this.addLoaderFunction(bundleItem, false);
            bundle += wrapped;
            _this.sourceMap.offsetLineNumber(wrapped);
        });
        bundle += this.createEntrypointFilenames() + "})(this);\n";
        bundle += this.sourceMap.getComment();
        this.validator.validate(bundle, this.bundleFile.name);
        fs.writeFile(this.bundleFile.name, bundle, function (error) {
            if (error) {
                throw error;
            }
            onMainBundleFileWritten();
        });
    };
    return Bundler;
}());
exports.Bundler = Bundler;
//# sourceMappingURL=bundler.js.map