UNPKG

@empathyco/x-components

Version:
286 lines (283 loc) • 10.1 kB
import { isFunction, forEach } from '@empathyco/x-utils'; import { reactive, createApp } from 'vue'; import { bus } from '../plugins/x-bus.js'; import { XPlugin } from '../plugins/x-plugin.js'; import { BaseXAPI } from './api/base-api.js'; /** * The purpose of this class is to offer a quick way to initialize the XComponents in a setup * project. It allows to receive all the options in {@link InstallXOptions} which is an extension * of {@link XPluginOptions} with all the options for the plugin and some options more. * * This class does multiple things: * 1. Install the {@link XPlugin} with the {@link XPluginOptions}. * 2. Creates the public {@link XAPI} and add it to global window. * 3. Creates the Vue Application for the customer project. * * The steps 2 & 3 are optional and depends on the options passed in {@link InstallXOptions}. * * @example The way to use this class is the next: * 1. Create the installer passing in the {@link InstallXOptions}. This only save the options: * * ``` * const installer = new XInstaller(installXOptions) * ``` * * 2. Initialize passing the {@link SnippetConfig}. This installs the plugin and creates the App. * There are 3 different ways to do this: * * 2.1 Using the created installer: * * ``` * installer.init(snippetConfig) * ``` * * 2.2 If the API option is enabled (`createAPI` is `true` in {@link InstallXOptions}, or * is not present as the default value is `true`) then this init step can be done with * the Public API: * * ``` * window.InterfaceX.init(snippetConfig) * ``` * * 2.3 When the script of the project build is loaded it searches for a global `initX` * variable that the customer must have in their website. This variable can be a * function that returns the {@link SnippetConfig} or an object that contains the * {@link SnippetConfig} itself: * * ``` * window.initX = function() { * return { * instance, * env, * scope, * lang, * uiLang, * currency, * consent, * documentDirection * }; * }; * ``` * * ``` * window.initX = { * instance, * env, * scope, * lang, * uiLang, * currency, * consent, * documentDirection * }; * ``` * * @public */ class XInstaller { /** * Receives the {@link InstallXOptions} and merges it with the default fallback options. Also * creates the public {@link XAPI}. * * @remarks Auto initializes the Vue application if window.initX is defined as a function or * object specifying the {@link SnippetConfig | snippet config}. * * * @param options - The {@link InstallXOptions}. * * @public */ constructor(options) { this.options = options; this.createAPI(); } /** * Creates the public {@link XAPI} using the `api` option from {@link InstallXOptions}. If this * `api` option is not passed, then a default {@link BaseXAPI} is created. To disable the API * creation the value `false` must be passed in the `api` option. * * @internal */ createAPI() { const { api } = this.options; if (api !== false) { this.api = api ?? new BaseXAPI(); // eslint-disable-next-line ts/no-misused-promises this.api.setInitCallback(this.init.bind(this)); this.api.setSnippetConfigCallback(this.updateSnippetConfig.bind(this)); this.api.setSnippetConfigGetter(this.getSnippetConfig.bind(this)); window.InterfaceX = this.api; } } /** * Retrieves the {@link SnippetConfig | snippet config} it is defined in the window.initX. * * @returns The snippet config if it is defined or undefined otherwise. * * @internal */ retrieveSnippetConfig() { if (typeof window.initX === 'function') { return window.initX(); } else if (typeof window.initX === 'object') { return window.initX; } } async init(snippetConfig = this.retrieveSnippetConfig()) { if (snippetConfig) { this.snippetConfig = reactive(this.normaliseSnippetConfig(snippetConfig)); this.createApp(); const bus = this.createBus(); const pluginOptions = this.getPluginOptions(); const plugin = this.installPlugin(pluginOptions, bus); await this.installExtraPlugins(bus); this.api?.setBus(bus); this.app.mount(this.getMountingTarget(this.options.domElement)); return { api: this.api, app: this.app, bus, plugin, }; } return Promise.resolve(); } /** * Creates the {@link XPluginOptions} object. * * @returns The {@link XPluginOptions} object. * * @internal */ getPluginOptions() { const { adapter, store, initialXModules, xModules, __PRIVATE__xModules } = this.options; return { adapter, store, xModules, initialXModules, __PRIVATE__xModules, }; } /** * This method returns the bus instance to be used in the {@link XPlugin} and in the {@link XAPI}. * It returns the `bus` parameter in the {@link InstallXOptions} or if not provided, then * creates a new instance of {@link @empathyco/x-bus#XPriorityBus | bus}. * * @returns XBus - The bus instance. * * @internal */ createBus() { return this.options.bus ?? bus; } /** * Creates and install the Vue Plugin. If `plugin` parameter is passed in the * {@link InstallXOptions}, then it is used. If not, then a new instance of {@link XPlugin} is * created and installed. * * @param pluginOptions - The {@link XPluginOptions} to passed as parameter to the install method * of the plugin. * @param bus - The XBus to be used to create the XPlugin. * * @returns Plugin<XPluginOption> - The plugin instance. * @internal */ installPlugin(pluginOptions, bus) { const plugin = this.options.plugin ?? new XPlugin(bus); this.app.use(plugin, pluginOptions); return plugin; } /** * Runs the installExtraPlugins callback defined in the {@link InstallXOptions} * to allow the user to install more plugins to the App. * * @param bus - The events bus used in the application. * @returns An empty promise. * @internal */ async installExtraPlugins(bus) { return Promise.resolve(this.options.installExtraPlugins?.({ app: this.app, snippet: this.snippetConfig, bus })); } /** * In the case that the `rootComponent` parameter is present in the {@link InstallXOptions}, * then a new Vue application is created using that component as root. * * @internal */ createApp() { if (this.options.rootComponent !== undefined) { this.app = createApp(this.options.rootComponent); this.app.provide('snippetConfig', this.snippetConfig); this.options.onCreateApp?.(this.app); } } /** * Transforms the snippet configuration. * - If `lang` is provided and `uiLang` is not, it sets `uiLang=lang`. * * @param snippetConfig - The snippet config to normalise. * @returns The normalised version of the given snippet config. * @internal */ normaliseSnippetConfig(snippetConfig) { if (snippetConfig.lang) { snippetConfig.uiLang ?? (snippetConfig.uiLang = snippetConfig.lang); } return snippetConfig; } /** * It returns the HTML element to mount the Vue Application. If the `domElement` parameter in * the {@link InstallXOptions} is an Element or an element selector, then this will be used. * The `domElement` can also be a function with the {@link SnippetConfig} as parameter which * returns an Element or element selector to use. * If it is not present, a new <div> Element is created and appended to the body. * * @param domElement - {@link InstallXOptions.domElement} Element, ShadowRoot, string or function * used to mount the Vue Application. * * @returns The Element or ShadowRoot to use as mounting target for the Vue Application. * @internal */ getMountingTarget(domElement) { if (isFunction(domElement)) { domElement = domElement(this.snippetConfig); } if (typeof domElement === 'string') { const target = document.querySelector(domElement); if (!target) { throw new Error(`XComponents app couldn't be mounted: Element "${domElement}" couldn't be found`); } return target; } return domElement ?? document.body.appendChild(document.createElement('div')); } /** * It updates all the provided properties from the current snippet config. * * @param newSnippetConfig - All the properties to be updated in the {@link SnippetConfig}. * * @internal */ updateSnippetConfig(newSnippetConfig) { if (!this.snippetConfig) { return; } forEach(this.normaliseSnippetConfig(newSnippetConfig), (name, value) => { this.snippetConfig[name] = value; }); } /** * Getter for the snippet config object. * * @returns The {@link NormalisedSnippetConfig | snippetConfig} object. * * @public */ getSnippetConfig() { return this.snippetConfig; } } export { XInstaller }; //# sourceMappingURL=x-installer.js.map