Repository: exadel-inc/esl
Pull request #1782 (2023-07-07 17:20:45)

fix: hotfix for writeable `is` field short initialization syntax


Patch:
diff --git a/src/modules/esl-base-element/core/esl-base-element.ts b/src/modules/esl-base-element/core/esl-base-element.ts
index 645339cf4..ca683ff49 100644
--- a/src/modules/esl-base-element/core/esl-base-element.ts
+++ b/src/modules/esl-base-element/core/esl-base-element.ts
@@ -11,6 +11,17 @@ import type {
 } from '../../esl-utils/dom/events';
 import type {ESLBaseComponent} from '../../esl-utils/abstract/component';
 
+/** Finalize tag name (`is`) for passed ESLBaseElement-based class */
+const finalize = (type: typeof ESLBaseElement, tagName: string): void => {
+  Object.defineProperty(type, 'is', {
+    get: () => tagName,
+    set(value) {
+      if (this === type) throw Error(`[ESL]: Cannot override ${type.name}.is property, the class is already registered`);
+      Object.defineProperty(this, 'is', {value, writable: true, configurable: true});
+    }
+  });
+};
+
 /**
  * Base class for ESL custom elements
  * Allows defining custom element with the optional custom tag name
@@ -108,16 +119,14 @@ export abstract class ESLBaseElement extends HTMLElement implements ESLBaseCompo
    */
   public static register(this: typeof ESLBaseElement, tagName?: string): void {
     tagName = tagName || this.is;
-    if (!tagName) {
-      throw new DOMException('[ESL]: Incorrect tag name', 'NotSupportedError');
-    }
+    if (!tagName) throw new DOMException('[ESL]: Incorrect tag name', 'NotSupportedError');
     const constructor: any = customElements.get(tagName);
     if (constructor && (constructor !== this || constructor.is !== tagName)) {
       throw new DOMException('[ESL]: Element tag already occupied or inconsistent', 'NotSupportedError');
     }
     if (constructor) return;
     customElements.define(tagName, this as any as CustomElementConstructor);
-    Object.defineProperty(this, 'is', {value: tagName, writable: false});
+    finalize(this, tagName);
   }
 
   /** Shortcut for `customElements.whenDefined(currentCustomElement)` */
@@ -132,5 +141,4 @@ export abstract class ESLBaseElement extends HTMLElement implements ESLBaseCompo
   public static create<T extends typeof ESLBaseElement>(this: T): InstanceType<T> {
     return document.createElement(this.is) as InstanceType<T>;
   }
-
 }


Test patch:
diff --git a/src/modules/esl-base-element/test/element.test.ts b/src/modules/esl-base-element/test/element.test.ts
index b00e6974e..01b7621cc 100644
--- a/src/modules/esl-base-element/test/element.test.ts
+++ b/src/modules/esl-base-element/test/element.test.ts
@@ -39,6 +39,19 @@ describe('ESLBaseElement', () => {
     expect(TestElement2.is).toBe('test-test');
   });
 
+  test('ESLBaseElement register validate (inheritance case)', () => {
+    expect(() => {
+      class TestIBase extends TestElement {
+        static override is = 'test-base-inh';
+      }
+      TestIBase.register();
+      class TestInherited extends TestElement {
+        static override is = 'test-child-inh';
+      }
+      TestInherited.register();
+    }).not.toThrowError();
+  });
+
   describe('ESLBaseElement prototype', () => {
     test('ESLBaseElement $attr', () => {
       const attrName = 'test-attr';