"use strict";
/**
 * @Copyright (c) Microsoft Corporation.  All rights reserved.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseComponentLoader = void 0;
var tslib_1 = require("tslib");
var sp_core_library_1 = require("@microsoft/sp-core-library");
var DebugManager_1 = require("../debug/DebugManager");
var ComponentStore_1 = tslib_1.__importDefault(require("../stores/ComponentStore"));
var ManifestStore_1 = tslib_1.__importDefault(require("../stores/ManifestStore"));
var componentConstants_1 = require("../utilities/componentConstants");
var killSwitches_1 = require("../tiny/common/killSwitches");
var platformLoader_1 = require("../tiny/common/platformLoader");
/**
 * The base class for a component loader.
 * Contains all implementation of the component loader that is agnostic to the internal module loader.
 *
 * @alpha
 */
var BaseComponentLoader = /** @class */ (function () {
    // Initialization
    /**
     * @internal
     */
    function BaseComponentLoader(serviceScope) {
        this._isInitialized = false;
        if (!BaseComponentLoader._headElement) {
            BaseComponentLoader._headElement = document.getElementsByTagName('head')[0];
        }
        this._serviceScope = serviceScope;
    }
    /**
     * This is called by the page scripts to start loading the framework. Do not call it from your own code.
     * @internal
     */
    BaseComponentLoader.prototype._startApplication = function (preloadedData) {
        // tslint:disable-next-line:no-any
        return platformLoader_1.startApplication(preloadedData, this._serviceScope).then(function (application) {
            // Normally non-SPFx environments locate their SPFx host application via a window variable
            // that our startup code assigns like this:
            //
            //   global.moduleLoaderPromise = global.spModuleLoader.start(spClientSidePageContext, handleFailure)
            //
            // However, in the case of the modern ListView, the "listview-spfx-host" loads after the main scripts,
            // which creates a race condition where window.moduleLoaderPromise sometimes might not be assigned yet
            // when their code tries to read it.  In that situation, they can register a callback like this:
            //
            // if (window['moduleLoaderPromise']) {
            //   window['moduleLoaderPromise'].then((application) => {
            //     doSomething(application);
            //   });
            // } else {
            //   window['_spLoaderCallback'] = function(application) {
            //     doSomething(application);
            //   };
            // }
            //
            /* tslint:disable:no-string-literal */
            if (window['_spLoaderCallback']) {
                var _spLoaderCallback = window['_spLoaderCallback']; // tslint:disable-line:no-any
                _spLoaderCallback(application);
            }
            /* tslint:enable:no-string-literal */
            return application;
        });
    };
    /**
     * Initializes the component loader.
     * Registers all manifests (including debug manifests, if it applies)
     * and sets up internal logic before allowing to load components.
     * @internal
     */
    BaseComponentLoader.prototype._initialize = function (preloadedData, bundledComponents, debugData) {
        if (this._isInitialized) {
            return;
        }
        // Set before the promise to ensure the async code is not called twice
        this._isInitialized = true;
        ManifestStore_1.default.instance.registerPreloadedManifests(preloadedData);
        // We need to register the debug manifests before overriding the components
        if (debugData.debugManifests) {
            if (!debugData.registerAsNonDebug && !killSwitches_1.isDangerouslyEnableDebugKSActivated()) {
                ManifestStore_1.default.instance.registerDebugManifests(debugData.debugManifests || []);
            }
            else {
                ManifestStore_1.default.instance.registerManifests(debugData.debugManifests || [], debugData.registerAsNonDebug);
            }
        }
        this._listViewHostWorkaround(preloadedData);
        // Pins the bundled components except when it's using a debug loader
        if (!debugData.debugLoader) {
            this._pinBundledComponents(bundledComponents);
        }
        this._overrideComponents(bundledComponents);
    };
    /**
     * Try and get a reference to a loaded component from a manifest.
     *
     * @param manifest - Manifest of the module to load.
     * @returns A reference to a component module by id and version or, if it does not exist, undefined.
     */
    BaseComponentLoader.prototype.tryGetLoadedComponent = function (manifest) {
        sp_core_library_1.Validate.isNotNullOrUndefined(manifest, 'manifest');
        return ComponentStore_1.default.instance.tryGetComponentReference(manifest.id, manifest.version);
    };
    /**
     * Resolve a component id and version, and load it.
     * If a manifest cannot be found, it requests the manifest to the server through the REST API.
     * If a manifest is not found and not present in the server, it rejects the promise.
     *
     * @param id      - The id of the component to load.
     * @param version - The version of the component to load. If version is not defined, the method
     *                    will load any version of the component.
     * @returns         A promise containing the loaded module.
     *
     * @alpha
     */
    BaseComponentLoader.prototype.loadComponentById = function (id, version) {
        var _this = this;
        var parsedId;
        try {
            sp_core_library_1.Validate.isNonemptyString(id, 'id');
            parsedId = sp_core_library_1.Guid.parse(id).toString();
        }
        catch (error) {
            return Promise.reject(error);
        }
        var manifest = ManifestStore_1.default.instance.tryGetManifest(parsedId, version);
        if (manifest) {
            return this.loadComponent(manifest);
        }
        else {
            return ManifestStore_1.default.instance
                .requestManifest(parsedId, version)
                .then(function (newManifest) { return _this.loadComponent(newManifest); });
        }
    };
    /**
     * Registers manifests in the manifest store.
     *
     * @param manifests - The manifests to register in the store.
     *
     * @alpha
     */
    BaseComponentLoader.prototype.registerManifests = function (manifests) {
        ManifestStore_1.default.instance.registerManifests(manifests, false);
    };
    Object.defineProperty(BaseComponentLoader.prototype, "_manifestReferences", {
        /**
         * All registered manifests.
         * Note that this returns the actual manifests, so modifying the
         * returned objects modifies the primary data, not a copy
         *
         * @readonly
         * @internal
         */
        get: function () {
            return ManifestStore_1.default.instance.getRegisteredManifests();
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Inserts a <link ... /> tag for a stylesheet.
     *
     * @param url - The CSS file URL.
     */
    BaseComponentLoader.prototype.loadCss = function (url) {
        sp_core_library_1.Validate.isNonemptyString(url, 'url');
        var linkTag = document.createElement('link');
        linkTag.rel = 'stylesheet';
        linkTag.type = 'text/css';
        linkTag.href = url;
        BaseComponentLoader._headElement.appendChild(linkTag);
    };
    // Internal methods
    /**
     * {@inheritDoc  ISPComponentLoader._loadDebugManifestsForWorkbench}
     *
     * @internal
     */
    BaseComponentLoader.prototype._loadDebugManifestsForWorkbench = function (manifestsFileUrl) {
        return DebugManager_1.DebugManager.loadAndRegisterManifestsFile(this, manifestsFileUrl, /* registerAsNonDebug */ true);
    };
    /**
     * Get a component manifest from the component id and version.
     *
     * @param id      - GUID id of the component.
     * @param version - Version of the component. If version is not defined, the method
     *                    will return the manifest for any version of the component.
     * @returns         Manifest for the component.
     *
     * @alpha
     */
    BaseComponentLoader.prototype.tryGetManifestById = function (id, version) {
        sp_core_library_1.Validate.isNonemptyString(id, 'id');
        var parsedId = sp_core_library_1.Guid.parse(id).toString();
        return ManifestStore_1.default.instance.tryGetManifest(parsedId, version);
    };
    /**
     * {@inheritdoc ManifestStore.requestManifest}
     *
     * @alpha
     */
    BaseComponentLoader.prototype.requestManifest = function (id, version) {
        sp_core_library_1.Validate.isNonemptyString(id, 'id');
        var parsedId = sp_core_library_1.Guid.parse(id).toString();
        return ManifestStore_1.default.instance.requestManifest(parsedId, version);
    };
    /**
     * {@inheritDoc  ISPComponentLoader._unloadComponents}
     *
     * @internal
     */
    BaseComponentLoader.prototype._unloadComponents = function () {
        var _this = this;
        ManifestStore_1.default.instance.getRegisteredManifests().forEach(function (manifest) {
            _this._unloadComponent(manifest);
        });
    };
    /**
     * {@inheritdoc ISPComponentLoader._preloadComponents}
     *
     * @internal
     */
    BaseComponentLoader.prototype._preloadComponents = function () {
        /* No-op. Only implemented in the tiny loader flow. */
    };
    /**
     * SystemJS component loader overrides this function with a workaround required for the loader to work with
     * ListView pages.
     * @virtual
     */
    BaseComponentLoader.prototype._listViewHostWorkaround = function (preloadedData) {
        // Do nothing
    };
    /**
     * Pins the manifest for each component that is bundled in the assembly.
     * See ManifestStore._pinManifest() for details about pinning.
     */
    BaseComponentLoader.prototype._pinBundledComponents = function (bundledComponents) {
        for (var id in bundledComponents) {
            // Different versions of React may be loaded by components at a later time, so we should not pin that manifest.
            // React / ReactDOM are the only non-SPFx, non-internal packages that we add on an assembly.
            if (bundledComponents.hasOwnProperty(id) && id !== componentConstants_1.reactComponentId && id !== componentConstants_1.reactDomComponentId) {
                ManifestStore_1.default.instance._pinManifest(id);
            }
        }
    };
    /**
     * Overrides component definitions with existing components.
     * This is used for the assemblies to set up all the data.
     * This is dependent on the internal module loader (SystemJS, RequireJS) so each implementation has to write its own.
     */
    BaseComponentLoader.prototype._overrideComponents = function (bundledComponents) {
        var _this = this;
        Object.keys(bundledComponents).forEach(function (key) {
            _this._overrideComponent(key, bundledComponents[key]);
        });
    };
    return BaseComponentLoader;
}());
exports.BaseComponentLoader = BaseComponentLoader;
//# sourceMappingURL=BaseComponentLoader.js.map