"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
// Copyright (c) Microsoft. All rights reserved.
var sp_core_library_1 = require("@microsoft/sp-core-library");
var sp_diagnostics_1 = require("@microsoft/sp-diagnostics");
var ErrorBuilder_1 = tslib_1.__importDefault(require("../error/ErrorBuilder"));
var SPLoaderError_1 = tslib_1.__importDefault(require("../error/SPLoaderError"));
var BaseComponentLoader_1 = require("../loader/BaseComponentLoader");
var loadComponent_1 = tslib_1.__importDefault(require("../loader/loadComponent"));
var ComponentStore_1 = tslib_1.__importDefault(require("../stores/ComponentStore"));
var ComponentOverrider_1 = tslib_1.__importDefault(require("../utilities/ComponentOverrider"));
var LoadComponentExecutor_1 = tslib_1.__importDefault(require("../utilities/LoadComponentExecutor"));
var telemetryConstants = tslib_1.__importStar(require("../utilities/telemetryConstants"));
var DeferredSystemJsFallbackLoader_1 = require("./DeferredSystemJsFallbackLoader");
var RequireJsLoader_1 = tslib_1.__importDefault(require("./RequireJsLoader"));
/**
 * The component loader for SPFx.
 * This implementation uses RequireJS as the internal module loader.
 *
 * @alpha
 */
var SPRequireJsComponentLoader = /** @class */ (function (_super) {
    tslib_1.__extends(SPRequireJsComponentLoader, _super);
    // Initialization
    /**
     * @internal
     */
    function SPRequireJsComponentLoader(serviceScope, useSecondaryCdn) {
        var _this = _super.call(this, serviceScope) || this;
        if (useSecondaryCdn) {
            sp_diagnostics_1._TraceLogger.logError(telemetryConstants.spRequireJsComponentLoader, new Error(telemetryConstants.usingSecondaryCdnEventName), telemetryConstants.usingSecondaryCdnEventName);
        }
        _this._requireJsLoader = serviceScope.consume(RequireJsLoader_1.default.serviceKey);
        _this._requireJsLoader.useSecondaryCdn = useSecondaryCdn;
        _this._loadComponentExecutor = new LoadComponentExecutor_1.default(_this._loadComponentWithExecutor.bind(_this));
        return _this;
    }
    // Public API
    SPRequireJsComponentLoader.prototype.loadScript = function (url, options) {
        var _a;
        try {
            sp_core_library_1.Validate.isNonemptyString(url, 'url');
        }
        catch (error) {
            return Promise.reject(error);
        }
        var qosMonitor = new sp_diagnostics_1._QosMonitor(telemetryConstants.loadScriptQosScenarioName);
        var rQosMonitor = new sp_diagnostics_1._QosMonitor(telemetryConstants.loadScriptWithRequireQosScenarioName);
        if (options === undefined) {
            options = {};
        }
        if (typeof options === 'string') {
            var error = ErrorBuilder_1.default.buildLoadScriptWithStringError();
            qosMonitor.writeExpectedFailure('OptionsAsString', error);
            return Promise.reject(error);
        }
        if (options.globalExportsName) {
            this._requireJsLoader.requireConfig({
                shim: (_a = {},
                    _a[url.replace(/\.js$/, '')] = {
                        exports: options.globalExportsName
                    },
                    _a)
            });
        }
        return this._requireJsLoader
            .requireLoad(url, options.globalExportsName)
            .then(function (module) {
            qosMonitor.writeSuccess();
            rQosMonitor.writeSuccess();
            return module;
        })
            .catch(function (e) {
            qosMonitor.writeUnexpectedFailure('RequireLoad', e);
            rQosMonitor.writeUnexpectedFailure('RequireLoad', e);
            throw e;
        });
    };
    SPRequireJsComponentLoader.prototype.loadComponent = function (manifest) {
        var _this = this;
        var qosMonitor;
        var qosExtraData;
        if (!manifest.isInternal) {
            qosMonitor = new sp_diagnostics_1._QosMonitor(telemetryConstants.loadComponentQosScenarioName);
            qosExtraData = this._buildQosExtraData(manifest);
        }
        return this._loadComponentExecutor
            .loadComponent(manifest)
            .then(function (componentLoaded) {
            var component = componentLoaded.component, requireJsError = componentLoaded.requireJsError;
            if (requireJsError && qosExtraData) {
                qosExtraData.loader = 'systemjs-fallback';
            }
            if (qosMonitor) {
                qosMonitor.writeSuccess(qosExtraData);
            }
            return component;
        })
            .catch(function (requireJsError) {
            return _this._handleLoadComponentError(requireJsError, manifest, qosMonitor, qosExtraData);
        });
    };
    // Internal/private API
    SPRequireJsComponentLoader.prototype._overrideComponent = function (componentId, componentModule) {
        ComponentOverrider_1.default.overrideComponent(componentId, componentModule, this._serviceScope, RequireJsLoader_1.default.serviceKey);
    };
    SPRequireJsComponentLoader.prototype._unloadComponent = function (manifest) {
        if (ComponentStore_1.default.instance.tryGetComponent(manifest.id, manifest.version)) {
            ComponentStore_1.default.instance.deleteComponent(manifest.id, manifest.version);
            this._requireJsLoader.requireDelete(manifest);
        }
    };
    /**
     * This functions tries to load the component with RequireJS, and if the killswitch is not enabled and the load fails,
     * will set up the component to be loaded with SystemJS.
     */
    SPRequireJsComponentLoader.prototype._loadComponentWithExecutor = function (manifest) {
        var _this = this;
        var loadWithRequireMonitor = new sp_diagnostics_1._QosMonitor(telemetryConstants.loadComponentWithRequireQosScenarioName);
        return loadComponent_1.default(manifest, this._requireJsLoader)
            .then(function (component) {
            loadWithRequireMonitor.writeSuccess();
            return {
                component: component
            };
        })
            .catch(function (requireJsError) {
            loadWithRequireMonitor.writeUnexpectedFailure('RequireLoad', requireJsError);
            var loadWithFallbackMonitor = new sp_diagnostics_1._QosMonitor(telemetryConstants.loadComponentWithFallbackSystemQosScenarioName);
            loadWithFallbackMonitor.extraData = { isInternal: !!manifest.isInternal };
            sp_diagnostics_1._TraceLogger.logVerbose(telemetryConstants.loadComponentLogSource, "RequireJS failed to load component \"" + manifest.id + "\". Trying again with SystemJS.");
            // Remove the component from the store to ensure that SystemJS doesn't assume it's already loaded
            ComponentStore_1.default.instance.deleteComponent(manifest.id, manifest.version);
            return _this._ensureFallback()
                .then(function () { return _this._systemJsFallbackLoader.loadComponent(manifest); })
                .then(function (component) {
                loadWithFallbackMonitor.writeSuccess();
                return {
                    component: component,
                    requireJsError: requireJsError
                };
            })
                .catch(function () {
                loadWithFallbackMonitor.writeUnexpectedFailure('FallbackLoad', requireJsError);
                // The systemjs error was already logged by the systemjs loader
                throw requireJsError;
            });
        });
    };
    SPRequireJsComponentLoader.prototype._ensureFallback = function () {
        var _this = this;
        if (this._systemJsFallbackLoader) {
            return Promise.resolve();
        }
        return DeferredSystemJsFallbackLoader_1.DeferredSystemJsFallbackLoader.getInstance()
            .load()
            .then(function () {
            _this._systemJsFallbackLoader = DeferredSystemJsFallbackLoader_1.DeferredSystemJsFallbackLoader.getInstance().create(_this._serviceScope);
            _this._loadComponentExecutor.setAlternativeExecutor(_this._systemJsFallbackLoader.executor);
            _this._systemJsFallbackLoader.executor.setAlternativeExecutor(_this._loadComponentExecutor);
        });
    };
    SPRequireJsComponentLoader.prototype._buildQosExtraData = function (manifest) {
        return {
            manifestId: manifest.id,
            version: manifest.version,
            alias: manifest.alias,
            isInternal: manifest.isInternal,
            isDebug: manifest._isDebug,
            loader: 'requirejs'
        };
    };
    SPRequireJsComponentLoader.prototype._handleLoadComponentError = function (error, manifest, qosMonitor, qosExtraData) {
        if (error instanceof SPLoaderError_1.default && error.isExpected && qosMonitor) {
            qosMonitor.writeExpectedFailure(undefined, error, qosExtraData);
        }
        else if (qosMonitor) {
            qosMonitor.writeUnexpectedFailure(undefined, error, qosExtraData);
        }
        // If it fails to load, the caller can then call loadComponent() again and not hit the cache
        ComponentStore_1.default.instance.deleteComponent(manifest.id, manifest.version);
        throw error;
    };
    return SPRequireJsComponentLoader;
}(BaseComponentLoader_1.BaseComponentLoader));
exports.default = SPRequireJsComponentLoader;
//# sourceMappingURL=SPRequireJsComponentLoader.js.map