import { onDebug } from '@atlassian/clientside-extensions-debug'; import type Registry from './registry'; import type { ParsedWebFragmentDescriptor } from './types'; const DEBUG_NAME = 'Registry - Web-fragment Element'; // This is a little sub-optimal - but we need the registry singleton here - and the best way to get it is to have it as a param. // Sadly there is no good way to pass constructor params to custom elements - so this is all one big ugly function. /** @since 2.1.0 */ export const registerCustomElement = ( registry: Registry, customElementName: string, // eslint-disable-next-line sonarjs/cognitive-complexity ) => { // already defined if (customElements.get(customElementName)) { onDebug(({ debug }) => ({ level: debug, message: `The custom-element "${customElementName}" has already been registered. Skipping registration…`, components: DEBUG_NAME, })); return; } class RegisterWebFragmentElement extends HTMLElement { private static _observedAttributes = ['extensions', 'location', 'handler'] as const; private attributesMap = new Map(); static get observedAttributes() { // eslint-disable-next-line no-underscore-dangle return RegisterWebFragmentElement._observedAttributes; } connectedCallback(): void { const parentNode = this.parentNode as HTMLElement; const location = this.attributesMap.get('location'); const handler = this.attributesMap.get('handler'); // ensure we have all the attributes if (!location) { onDebug(({ error }) => ({ level: error, message: `No location specified. Aborting registration…`, components: DEBUG_NAME, })); return; } if (!handler) { onDebug(({ error }) => ({ level: error, message: `No handler specified. Aborting registration…`, components: DEBUG_NAME, })); return; } // something is wrong if (!parentNode) { onDebug(({ error }) => ({ level: error, message: `No parent element found for registration of handler: "${handler}" at location: "${location}". Aborting registration…`, components: DEBUG_NAME, meta: { location, handler, }, })); return; } // we dont need to be in the dom parentNode.removeChild(this); // technically we dont need extensions - so we can just pass an empty array let extensions: DescriptorType[] = []; if (this.attributesMap.has('extensions')) { try { extensions = JSON.parse(this.attributesMap.get('extensions')!); } catch (e) { onDebug(({ error }) => ({ level: error, message: `Error with extensions payload at location: "${location}". Failed to parse extensions as JSON. Please ensure you provide your Web-Fragments correctly formatted!`, components: DEBUG_NAME, meta: { extensions, location, handler, }, })); } } const renderHandler = registry.getHandler(handler); if (renderHandler) { try { renderHandler(parentNode, location, extensions); } catch (e) { onDebug(({ error }) => ({ level: error, message: `Execution of render-handler "${handler}" at location "${location}" failed.`, components: DEBUG_NAME, meta: { location, handler, error: e, }, })); } } else { onDebug(({ error }) => ({ level: error, message: `No handler registered with name: "${handler}". Failed to complete registration…`, components: DEBUG_NAME, })); } } attributeChangedCallback(name: string, oldValue: string, newValue: string): void { if (oldValue === newValue) { return; } this.attributesMap.set(name, newValue); } } customElements.define(customElementName, RegisterWebFragmentElement); };