diff --git a/integration/hello_server/Greeter_component.ts b/integration/hello_server/Greeter_component.ts index 46c8fd61595..a1907377ea3 100644 --- a/integration/hello_server/Greeter_component.ts +++ b/integration/hello_server/Greeter_component.ts @@ -7,7 +7,7 @@ */ import type { GreeterProps } from './Greeter'; -import { Component, QRL } from '@builder.io/qwik'; +import { Component } from '@builder.io/qwik'; /** * @fileoverview @@ -32,7 +32,6 @@ export interface GreeterState { * non-serializable objects. Component has shared behavior. */ export class GreeterComponent extends Component { - static $templateQRL = QRL`./Greeter_template`; // Inherited properties from `Component` // $host: Element; // $state: GreeterState; diff --git a/integration/todo/ui/Header_component.ts b/integration/todo/ui/Header_component.ts index f396c5949bc..b0d09012961 100644 --- a/integration/todo/ui/Header_component.ts +++ b/integration/todo/ui/Header_component.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ -import { Component, QRL } from '@builder.io/qwik'; +import { Component } from '@builder.io/qwik'; import type { HeaderProps } from './Header'; interface HeaderState { @@ -14,7 +14,6 @@ interface HeaderState { } export class HeaderComponent extends Component { - static $templateQRL = QRL`ui:/Header_template`; $newState() { return { text: '' }; } diff --git a/integration/todo/ui/Item_component.ts b/integration/todo/ui/Item_component.ts index 5aa2fa0b19d..548a260ec88 100644 --- a/integration/todo/ui/Item_component.ts +++ b/integration/todo/ui/Item_component.ts @@ -6,14 +6,12 @@ * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ -import { Component, QRL } from '@builder.io/qwik'; +import { Component } from '@builder.io/qwik'; import type { ItemProps } from './Item'; interface ItemState {} export class ItemComponent extends Component { - static $templateQRL = QRL`ui:/Item_template`; - editing = false; $newState() { return {}; diff --git a/src/core/api.md b/src/core/api.md index fa2747900af..49e020c77d7 100644 --- a/src/core/api.md +++ b/src/core/api.md @@ -10,13 +10,11 @@ export class Component { $init(): Promise | void; // (undocumented) static $new>(this: { - $templateQRL: QRL; new (...args: any[]): COMP; }, hostElement: Element): Promise; $newState(props: PROPS): Promise | STATE; $props: PROPS; $state: STATE; - static $templateQRL: QRL; constructor(hostElement: Element, props: PROPS, state: STATE | null); } @@ -28,8 +26,6 @@ export type ComponentChildren = ComponentChild[] | ComponentChild; // @public export interface ComponentConstructor> { - // (undocumented) - $templateQRL: QRL; // (undocumented) new (hostElement: Element, props: ComponentPropsOf, state: ComponentStateOf | null): COMP; } @@ -251,7 +247,6 @@ export interface InjectedFunction(...args: [ { - $templateQRL: QRL; new (hostElement: Element, props: any, state: any): SELF; } | null, ...ARGS, diff --git a/src/core/component/component.ts b/src/core/component/component.ts index 74e46094459..31ed137bd07 100644 --- a/src/core/component/component.ts +++ b/src/core/component/component.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ -import type { QRL } from '../import/qrl'; import { QError, qError } from '../error/error'; import { AttributeMarker } from '../util/markers'; import { getInjector } from '../injector/element_injector'; @@ -35,14 +34,8 @@ import { getInjector } from '../injector/element_injector'; * @public */ export class Component { - /** - * Pointer to template to verify that the component is attached to the right DOM location. - */ - static $templateQRL: QRL = null!; - static $new>( this: { - $templateQRL: QRL; new (...args: any[]): COMP; }, hostElement: Element @@ -51,11 +44,6 @@ export class Component { const componentConstructor = this as any as ComponentConstructor; const componentTemplate = hostElement.getAttribute(AttributeMarker.ComponentTemplate); if (!componentTemplate) { - hostElement.setAttribute( - AttributeMarker.ComponentTemplate, - componentConstructor.$templateQRL as any - ); - } else if (componentTemplate !== (componentConstructor.$templateQRL as any)) { // TODO: Needs tests for error condition for attaching component to element which already has a component throw new Error('Write proper error'); } @@ -188,7 +176,6 @@ export type ComponentPropsOf> = SERVICE exte * @public */ export interface ComponentConstructor> { - $templateQRL: QRL; new ( hostElement: Element, props: ComponentPropsOf, @@ -202,5 +189,5 @@ export interface ComponentConstructor> { * @internal */ export function isComponent(object: any): object is Component { - return typeof object?.constructor?.$templateQRL === 'string'; + return object instanceof Component; } diff --git a/src/core/component/component.unit.ts b/src/core/component/component.unit.ts index af5526bb4ec..fe6df1f57cc 100644 --- a/src/core/component/component.unit.ts +++ b/src/core/component/component.unit.ts @@ -7,7 +7,7 @@ */ import { ComponentFixture } from '@builder.io/qwik/testing'; -import { GreeterComponent } from '../util/test_component_fixture'; +import { GreeterComponent, GreeterComponentTemplate } from '../util/test_component_fixture'; import { AttributeMarker } from '../util/markers'; import { Component } from './component'; import { injectMethod } from '../injector/inject'; @@ -15,10 +15,7 @@ import { injectMethod } from '../injector/inject'; describe('component', () => { it('should declare a component', async () => { const fixture = new ComponentFixture(); - fixture.host.setAttribute( - AttributeMarker.ComponentTemplate, - String(GreeterComponent.$templateQRL) - ); + fixture.host.setAttribute(AttributeMarker.ComponentTemplate, String(GreeterComponentTemplate)); fixture.host.setAttribute('salutation', 'Hello'); fixture.host.setAttribute('name', 'World'); const greeter = await fixture.injector.getComponent(GreeterComponent); diff --git a/src/core/error/error.ts b/src/core/error/error.ts index c5520ec369d..34243a84645 100644 --- a/src/core/error/error.ts +++ b/src/core/error/error.ts @@ -53,7 +53,6 @@ export const enum QError { Component_noProperty_propName_props_host = 404, Component_notFound_component = 405, Component_doesNotMatch_component_actual = 406, - Component_missingTemplateQRL_component = 407, Component_noState_component_props = 408, // Provider 500-599 Provider_unrecognizedFormat_value = 500, @@ -165,9 +164,7 @@ function codeToText(code: QError): string { "Property '{}' not found in '{}' on component '{}'.", [QError.Component_notFound_component]: "Unable to find '{}' component.", [QError.Component_doesNotMatch_component_actual]: - "Requesting component '{}' does not match existing component '{}'. Verify that the two components have distinct '$templateQRL's.", - [QError.Component_missingTemplateQRL_component]: - "Expecting Component '{}' to have static '$templateQRL' property, but none was found.", + "Requesting component type '{}' does not match existing component instance '{}'.", [QError.Component_noState_component_props]: "Unable to create state for component '{}' with props '{}' because no state found and '$newState()' method was not defined on component.", ////////////// diff --git a/src/core/event/inject_event_handler.ts b/src/core/event/inject_event_handler.ts index 15fdce7f3d5..1707e671b4c 100644 --- a/src/core/event/inject_event_handler.ts +++ b/src/core/event/inject_event_handler.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE */ -import type { QRL } from '../import/qrl'; import type { InjectedFunction, ProviderReturns } from '../injector/types'; import { qDev } from '../util/qdev'; import { EventInjector } from './event_injector'; @@ -41,7 +40,6 @@ import type { EventHandler } from './types'; export function injectEventHandler( ...args: [ { - $templateQRL: QRL; new (hostElement: Element, props: any, state: any): SELF; } | null, ...ARGS, diff --git a/src/core/event/inject_event_handler.unit.ts b/src/core/event/inject_event_handler.unit.ts index 8c9a8ee9ef6..bea58bf5f6b 100644 --- a/src/core/event/inject_event_handler.unit.ts +++ b/src/core/event/inject_event_handler.unit.ts @@ -21,7 +21,7 @@ describe('injectEventHandler', () => { it('should support component injection', async () => { const event = 'EVENT' as any as Event; const url = new URL('http://localhost/path?a=b&c=d'); - fixture.host.setAttribute(AttributeMarker.ComponentTemplate, String(MyComponent.$templateQRL)); + fixture.host.setAttribute(AttributeMarker.ComponentTemplate, String(MyComponentTemplate)); const fn = injectEventHandler( MyComponent, @@ -42,8 +42,8 @@ describe('injectEventHandler', () => { }); }); +const MyComponentTemplate: QRL = 'myComponentQRL' as any; class MyComponent extends Component { - static $templateQRL: QRL = 'myComponentQRL' as any; $newState() { return {}; } diff --git a/src/core/injector/base_injector.unit.ts b/src/core/injector/base_injector.unit.ts index 86d5f4197e5..72661afe182 100644 --- a/src/core/injector/base_injector.unit.ts +++ b/src/core/injector/base_injector.unit.ts @@ -82,7 +82,7 @@ describe('BaseInjector', () => { it('should call injected method', async () => { const log: (string | MyComponent)[] = []; - fixture.host.setAttribute(AttributeMarker.ComponentTemplate, MyComponent.$templateQRL as any); + fixture.host.setAttribute(AttributeMarker.ComponentTemplate, String(MyComponentTemplate)); const injectedFn = injectMethod( MyComponent, provideConst('arg0'), // @@ -103,7 +103,7 @@ describe('BaseInjector', () => { it('should call injected method (as default)', async () => { const log: (string | MyComponent)[] = []; - fixture.host.setAttribute(AttributeMarker.ComponentTemplate, MyComponent.$templateQRL as any); + fixture.host.setAttribute(AttributeMarker.ComponentTemplate, String(MyComponentTemplate)); const injectedFn = injectMethod( MyComponent, provideConst('arg0'), // @@ -126,10 +126,7 @@ describe('BaseInjector', () => { describe('error', () => { it('should include declare context when throwing error', async () => { - fixture.host.setAttribute( - AttributeMarker.ComponentTemplate, - MyComponent.$templateQRL as any - ); + fixture.host.setAttribute(AttributeMarker.ComponentTemplate, String(MyComponentTemplate)); const injectedFn = injectMethod( MyComponent, () => Promise.reject('ProviderRejection'), @@ -212,8 +209,8 @@ function provideConst(value: T): Provider { class MyClass {} +const MyComponentTemplate = 'test:/injector/base_injector.unit#template' as any as QRL; class MyComponent extends Component { - static $templateQRL = 'test:/injector/base_injector.unit#template' as any as QRL; $newState() { return {}; } diff --git a/src/core/injector/element_injector.ts b/src/core/injector/element_injector.ts index bf2c080e6d6..0150171e98c 100644 --- a/src/core/injector/element_injector.ts +++ b/src/core/injector/element_injector.ts @@ -64,11 +64,7 @@ export class ElementInjector extends BaseInjector { const elementQRL: QRL | null = injector.element.getAttribute( AttributeMarker.ComponentTemplate ) as any; - const $templateQRL = componentType.$templateQRL; - if (!$templateQRL) { - throw qError(QError.Component_missingTemplateQRL_component, componentType); - } - if (elementQRL === $templateQRL) { + if (elementQRL) { let component: COMP = this.component as COMP; if (component) { if (component instanceof componentType) { diff --git a/src/core/injector/element_injector.unit.ts b/src/core/injector/element_injector.unit.ts index 6ca5949ecc3..640d8283bf0 100644 --- a/src/core/injector/element_injector.unit.ts +++ b/src/core/injector/element_injector.unit.ts @@ -12,7 +12,12 @@ import type { EntityKey } from '../entity/entity_key'; import { stringifyDebug } from '../error/stringify'; import { QRL } from '../import/qrl'; import { Entity, Injector } from '../index'; -import { Greeter, GreeterComponent, GreeterProps } from '../util/test_component_fixture'; +import { + Greeter, + GreeterComponent, + GreeterComponentTemplate, + GreeterProps, +} from '../util/test_component_fixture'; import { ElementFixture, serializeState } from '@builder.io/qwik/testing'; import { AttributeMarker } from '../util/markers'; import { getClosestInjector, getInjector } from './element_injector'; @@ -27,10 +32,7 @@ describe('ElementInjector', () => { describe('getComponent', () => { it('should materialize component and return same instance', async () => { - fixture.host.setAttribute( - AttributeMarker.ComponentTemplate, - GreeterComponent.$templateQRL as any - ); + fixture.host.setAttribute(AttributeMarker.ComponentTemplate, GreeterComponentTemplate as any); const component = await hostInjector.getComponent(GreeterComponent); expect(component).toBeInstanceOf(GreeterComponent); expect(stringifyDebug(component.$host)).toEqual(stringifyDebug(fixture.host)); @@ -41,7 +43,7 @@ describe('ElementInjector', () => { it('should walk up the tree and find materialize component', async () => { fixture.superParent.setAttribute( AttributeMarker.ComponentTemplate, - GreeterComponent.$templateQRL as any + GreeterComponentTemplate as any ); const component = await hostInjector.getComponent(GreeterComponent); expect(component).toBeInstanceOf(GreeterComponent); @@ -50,7 +52,7 @@ describe('ElementInjector', () => { it('should return the same promise instance', () => { fixture.superParent.setAttribute( AttributeMarker.ComponentTemplate, - GreeterComponent.$templateQRL as any + GreeterComponentTemplate as any ); const component1 = hostInjector.getComponent(GreeterComponent); const component2 = hostInjector.getComponent(GreeterComponent); @@ -60,7 +62,7 @@ describe('ElementInjector', () => { it('should materialize from attribute state', async () => { fixture.host.setAttribute( AttributeMarker.ComponentTemplate, - GreeterComponent.$templateQRL as any + GreeterComponentTemplate as any ); fixture.host.setAttribute( AttributeMarker.ComponentState, @@ -73,7 +75,7 @@ describe('ElementInjector', () => { it('should materialize from $newState', async () => { fixture.host.setAttribute( AttributeMarker.ComponentTemplate, - GreeterComponent.$templateQRL as any + GreeterComponentTemplate as any ); fixture.host.setAttribute('salutation', 'Hello'); fixture.host.setAttribute('name', 'World'); @@ -85,7 +87,7 @@ describe('ElementInjector', () => { it('should save state to attribute state', async () => { fixture.host.setAttribute( AttributeMarker.ComponentTemplate, - GreeterComponent.$templateQRL as any + GreeterComponentTemplate as any ); const component = await hostInjector.getComponent(GreeterComponent); component.$state = { greeting: 'save me' }; @@ -97,25 +99,18 @@ describe('ElementInjector', () => { }); describe('error', () => { it('should throw if component does not match', async () => { - fixture.parent.setAttribute(AttributeMarker.ComponentTemplate, 'wrongQRL'); expect(() => hostInjector.getComponent(GreeterComponent)).toThrow( "COMPONENT-ERROR(Q-405): Unable to find 'GreeterComponent' component." ); }); - it('should throw if two components have same $templateQRLs', async () => { + it('should throw if two components are of different types', async () => { fixture.superParent.setAttribute( AttributeMarker.ComponentTemplate, - GreeterComponent.$templateQRL as any + GreeterComponentTemplate as any ); await hostInjector.getComponent(GreeterComponent); expect(() => hostInjector.getComponent(GreeterShadowComponent)).toThrow( - "COMPONENT-ERROR(Q-406): Requesting component 'GreeterShadowComponent' does not match existing component 'GreeterComponent'. Verify that the two components have distinct '$templateQRL's." - ); - }); - it('should throw if two components is missing $templateQRL', async () => { - class MissingQRL {} - expect(() => hostInjector.getComponent(MissingQRL as any)).toThrow( - "COMPONENT-ERROR(Q-407): Expecting Component 'MissingQRL' to have static '$templateQRL' property, but none was found." + "COMPONENT-ERROR(Q-406): Requesting component type 'GreeterShadowComponent' does not match existing component instance 'GreeterComponent'." ); }); }); @@ -223,7 +218,7 @@ describe('ElementInjector', () => { }); class GreeterShadowComponent extends Component { - static $templateQRL: QRL = GreeterComponent.$templateQRL; + static $templateQRL: QRL = GreeterComponentTemplate; } interface RegardsProps { diff --git a/src/core/injector/inject.unit.ts b/src/core/injector/inject.unit.ts index 046fa8ccd68..c91c872f074 100644 --- a/src/core/injector/inject.unit.ts +++ b/src/core/injector/inject.unit.ts @@ -7,7 +7,6 @@ */ import { injectEventHandler } from '../event/inject_event_handler'; -import type { QRL } from '../import/qrl'; import { createGlobal, ElementFixture } from '@builder.io/qwik/testing'; import { AttributeMarker } from '../util/markers'; import { getInjector } from './element_injector'; @@ -62,7 +61,6 @@ describe('injectEventHandler', () => { it('should inject this', async () => { class MyComp { - static $templateQRL = './comp' as any as QRL; $state = undefined; myComp: boolean = true; $newState() {} diff --git a/src/core/injector/types.ts b/src/core/injector/types.ts index 822b99d482b..8c0aeae7b95 100644 --- a/src/core/injector/types.ts +++ b/src/core/injector/types.ts @@ -84,11 +84,11 @@ export interface Injector { * * Use this function for retrieving/materialize a component instance. * The function starts with the current `element` and walks up until it finds - * an element with `AttributeMarker.ComponentTemplate` which matches the - * `componentType.$templateQRL`. Once found it than tries to retrieve existing - * component (or materialize it from the `AttributeMarker.ComponentState`). - * Because creation of component may involve invoking `Component.$newState` - * which is asynchronous the method itself is asynchronous. + * an element with `AttributeMarker.ComponentTemplate`. Once found it than + * tries to retrieve existing component (or materialize it from the + * `AttributeMarker.ComponentState`). Because creation of component may involve + * invoking `Component.$newState` which is asynchronous the method itself is + * asynchronous. * * @param componentType - Component type to retrieve. */ diff --git a/src/core/render/jsx/mark_dirty.unit.ts b/src/core/render/jsx/mark_dirty.unit.ts index 56130ca5a98..12933a5869f 100644 --- a/src/core/render/jsx/mark_dirty.unit.ts +++ b/src/core/render/jsx/mark_dirty.unit.ts @@ -7,7 +7,11 @@ */ import { stringifyDebug } from '../../error/stringify'; -import { GreeterComponent, PersonEntity } from '../../util/test_component_fixture'; +import { + GreeterComponent, + GreeterComponentTemplate, + PersonEntity, +} from '../../util/test_component_fixture'; import { ElementFixture, MockDocument, getTestPlatform } from '@builder.io/qwik/testing'; import { AttributeMarker } from '../../util/markers'; import { markDirty, markEntityDirty, scheduleRender, toAttrQuery } from './mark_dirty'; @@ -21,6 +25,7 @@ describe('mark_dirty', () => { fixture = new ElementFixture(); host = fixture.host; doc = fixture.document; + fixture.host.setAttribute(AttributeMarker.ComponentTemplate, String(GreeterComponentTemplate)); greeterComponent = await GreeterComponent.$new(fixture.host); }); diff --git a/src/core/util/test_component_fixture.tsx b/src/core/util/test_component_fixture.tsx index bbb60cd2250..d276d405d02 100644 --- a/src/core/util/test_component_fixture.tsx +++ b/src/core/util/test_component_fixture.tsx @@ -16,9 +16,12 @@ export interface Greeter { greeting: string; } -export class GreeterComponent extends Component { - static $templateQRL = QRL`${toFileUrl(__filename).replace(/\.tsx$/, '#greeterTemplate')}`; +export const GreeterComponentTemplate = QRL`${toFileUrl(__filename).replace( + /\.tsx$/, + '#greeterTemplate' +)}`; +export class GreeterComponent extends Component { greeting: string = null!; async $init() {