Skip to content

Commit e94ec15

Browse files
authored
fix(engine-core): use class name when invoking toString() on component (salesforce#1963)
1 parent adb0354 commit e94ec15

File tree

5 files changed

+34
-54
lines changed

5 files changed

+34
-54
lines changed

packages/@lwc/engine-core/src/framework/component.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,24 @@ export interface ComponentMeta {
3434
readonly template?: Template;
3535
}
3636

37-
const signedComponentToMetaMap: Map<ComponentConstructor, ComponentMeta> = new Map();
37+
const signedTemplateMap: Map<ComponentConstructor, Template> = new Map();
3838

3939
/**
4040
* INTERNAL: This function can only be invoked by compiled code. The compiler
4141
* will prevent this function from being imported by userland code.
4242
*/
4343
export function registerComponent(
4444
Ctor: ComponentConstructor,
45-
{ name, tmpl: template }: { name: string; tmpl: Template }
45+
{ tmpl }: { tmpl: Template }
4646
): ComponentConstructor {
47-
signedComponentToMetaMap.set(Ctor, { name, template });
48-
// chaining this method as a way to wrap existing
49-
// assignment of component constructor easily, without too much transformation
47+
signedTemplateMap.set(Ctor, tmpl);
48+
// chaining this method as a way to wrap existing assignment of component constructor easily,
49+
// without too much transformation
5050
return Ctor;
5151
}
5252

53-
export function getComponentRegisteredMeta(Ctor: ComponentConstructor): ComponentMeta | undefined {
54-
return signedComponentToMetaMap.get(Ctor);
53+
export function getComponentRegisteredTemplate(Ctor: ComponentConstructor): Template | undefined {
54+
return signedTemplateMap.get(Ctor);
5555
}
5656

5757
export function createComponent(vm: VM, Ctor: ComponentConstructor) {

packages/@lwc/engine-core/src/framework/def.ts

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,7 @@ import {
2828
} from '@lwc/shared';
2929
import { getAttrNameFromPropName } from './attributes';
3030
import { EmptyObject } from './utils';
31-
import {
32-
ComponentConstructor,
33-
ErrorCallback,
34-
ComponentMeta,
35-
getComponentRegisteredMeta,
36-
} from './component';
31+
import { ComponentConstructor, ErrorCallback, getComponentRegisteredTemplate } from './component';
3732
import { Template } from './template';
3833
import { BaseLightningElement, lightningBasedDescriptors } from './base-lightning-element';
3934
import { PropType, getDecoratorsMeta } from './decorators/register';
@@ -68,11 +63,11 @@ export interface ComponentDef {
6863

6964
const CtorToDefMap: WeakMap<any, ComponentDef> = new WeakMap();
7065

71-
function getCtorProto(Ctor: any, subclassComponentName: string): ComponentConstructor {
66+
function getCtorProto(Ctor: ComponentConstructor): ComponentConstructor {
7267
let proto: ComponentConstructor | null = getPrototypeOf(Ctor);
7368
if (isNull(proto)) {
7469
throw new ReferenceError(
75-
`Invalid prototype chain for ${subclassComponentName}, you must extend LightningElement.`
70+
`Invalid prototype chain for ${Ctor.name}, you must extend LightningElement.`
7671
);
7772
}
7873
// covering the cases where the ref is circular in AMD
@@ -81,7 +76,7 @@ function getCtorProto(Ctor: any, subclassComponentName: string): ComponentConstr
8176
if (process.env.NODE_ENV !== 'production') {
8277
if (isNull(p)) {
8378
throw new ReferenceError(
84-
`Circular module dependency for ${subclassComponentName}, must resolve to a constructor that extends LightningElement.`
79+
`Circular module dependency for ${Ctor.name}, must resolve to a constructor that extends LightningElement.`
8580
);
8681
}
8782
}
@@ -94,13 +89,8 @@ function getCtorProto(Ctor: any, subclassComponentName: string): ComponentConstr
9489
return proto!;
9590
}
9691

97-
function createComponentDef(
98-
Ctor: ComponentConstructor,
99-
meta: ComponentMeta,
100-
subclassComponentName: string
101-
): ComponentDef {
92+
function createComponentDef(Ctor: ComponentConstructor): ComponentDef {
10293
if (process.env.NODE_ENV !== 'production') {
103-
// local to dev block
10494
const ctorName = Ctor.name;
10595
// Removing the following assert until https://bugs.webkit.org/show_bug.cgi?id=190140 is fixed.
10696
// assert.isTrue(ctorName && isString(ctorName), `${toString(Ctor)} should have a "name" property with string value, but found ${ctorName}.`);
@@ -110,8 +100,6 @@ function createComponentDef(
110100
);
111101
}
112102

113-
const { name } = meta;
114-
let { template } = meta;
115103
const decoratorsMeta = getDecoratorsMeta(Ctor);
116104
const {
117105
apiFields,
@@ -130,13 +118,12 @@ function createComponentDef(
130118
errorCallback,
131119
render,
132120
} = proto;
133-
const superProto = getCtorProto(Ctor, subclassComponentName);
134-
const superDef: ComponentDef =
135-
(superProto as any) !== BaseLightningElement
136-
? getComponentInternalDef(superProto, subclassComponentName)
121+
const superProto = getCtorProto(Ctor);
122+
const superDef =
123+
superProto !== BaseLightningElement
124+
? getComponentInternalDef(superProto)
137125
: lightingElementDef;
138-
const SuperBridge = isNull(superDef) ? BaseBridgeElement : superDef.bridge;
139-
const bridge = HTMLBridgeElementFactory(SuperBridge, keys(apiFields), keys(apiMethods));
126+
const bridge = HTMLBridgeElementFactory(superDef.bridge, keys(apiFields), keys(apiMethods));
140127
const props: PropertyDescriptorMap = assign(create(null), superDef.props, apiFields);
141128
const propsConfig = assign(create(null), superDef.propsConfig, apiFieldsConfig);
142129
const methods: PropertyDescriptorMap = assign(create(null), superDef.methods, apiMethods);
@@ -151,7 +138,9 @@ function createComponentDef(
151138
renderedCallback = renderedCallback || superDef.renderedCallback;
152139
errorCallback = errorCallback || superDef.errorCallback;
153140
render = render || superDef.render;
154-
template = template || superDef.template;
141+
142+
const template = getComponentRegisteredTemplate(Ctor) || superDef.template;
143+
const name = Ctor.name || superDef.name;
155144

156145
// installing observed fields into the prototype.
157146
defineProperties(proto, observedFields);
@@ -218,7 +207,7 @@ export function isComponentConstructor(ctor: unknown): ctor is ComponentConstruc
218207
return false;
219208
}
220209

221-
export function getComponentInternalDef(Ctor: unknown, name?: string): ComponentDef {
210+
export function getComponentInternalDef(Ctor: unknown): ComponentDef {
222211
let def = CtorToDefMap.get(Ctor);
223212

224213
if (isUndefined(def)) {
@@ -237,16 +226,7 @@ export function getComponentInternalDef(Ctor: unknown, name?: string): Component
237226
);
238227
}
239228

240-
let meta = getComponentRegisteredMeta(Ctor);
241-
if (isUndefined(meta)) {
242-
// TODO [#1295]: remove this workaround after refactoring tests
243-
meta = {
244-
template: undefined,
245-
name: Ctor.name,
246-
};
247-
}
248-
249-
def = createComponentDef(Ctor, meta, name || Ctor.name);
229+
def = createComponentDef(Ctor);
250230
CtorToDefMap.set(Ctor, def);
251231
}
252232

@@ -291,8 +271,8 @@ interface PublicComponentDef {
291271
* EXPERIMENTAL: This function allows for the collection of internal component metadata. This API is
292272
* subject to change or being removed.
293273
*/
294-
export function getComponentDef(Ctor: any, subclassComponentName?: string): PublicComponentDef {
295-
const def = getComponentInternalDef(Ctor, subclassComponentName);
274+
export function getComponentDef(Ctor: any): PublicComponentDef {
275+
const def = getComponentInternalDef(Ctor);
296276
// From the internal def object, we need to extract the info that is useful
297277
// for some external services, e.g.: Locker Service, usually, all they care
298278
// is about the shape of the constructor, the internals of it are not relevant

packages/integration-karma/test/component/LightningElement.render/index.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ it(`throws an error if returns an invalid template`, () => {
3131
document.body.appendChild(elm);
3232
}).toThrowError(
3333
Error,
34-
/Invalid template returned by the render\(\) method on .+\. It must return an imported template \(e\.g\.: `import html from "\.\/undefined.html"`\), instead, it has returned: .+\./
34+
/Invalid template returned by the render\(\) method on .+\. It must return an imported template \(e\.g\.: `import html from "\.\/DynamicTemplate.html"`\), instead, it has returned: .+\./
3535
);
3636
});
3737

packages/integration-karma/test/component/LightningElement.toString/index.spec.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import { createElement } from 'lwc';
33
import Anonymous from 'x/anonymous';
44
import Named from 'x/named';
55

6-
// TODO [#1033]: with the transformation done by the compiler, the class name is removed, producing "[object undefined]"
7-
xit('should rely on the constructor name', () => {
6+
it('should rely on the constructor name', () => {
87
const elm = createElement('x-named', { is: Named });
98
expect(elm.getToString()).toBe('[object MyFancyComponent]');
109
});
1110

12-
// TODO [#1033]: open issue return "[object ]" on Safari
13-
xit('should fallback to BaseLightningElement if constructor has no name', () => {
14-
const elm = createElement('x-anonymous', { is: Anonymous });
15-
expect(elm.getToString()).toBe('[object BaseLightningElement]');
16-
});
11+
if (process.env.COMPAT !== true) {
12+
it('should fallback to BaseLightningElementConstructor if constructor has no name', () => {
13+
const elm = createElement('x-anonymous', { is: Anonymous });
14+
expect(elm.getToString()).toBe('[object BaseLightningElementConstructor]');
15+
});
16+
}

packages/integration-karma/test/template/directive-for-each/index.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ it('should throw an error when the passing a non iterable', () => {
6464

6565
// TODO [#1283]: Improve this error message. The vm should not be exposed and the message is not helpful.
6666
expect(() => document.body.appendChild(elm)).toThrowError(
67-
/Invalid template iteration for value `\[object (ProxyObject|Object)\]` in \[object:vm undefined \(\d+\)\]. It must be an array-like object and not `null` nor `undefined`./
67+
/Invalid template iteration for value `\[object (ProxyObject|Object)\]` in \[object:vm Test \(\d+\)\]. It must be an array-like object and not `null` nor `undefined`./
6868
);
6969
});
7070

@@ -81,6 +81,6 @@ it('throws when passing an invalid key', () => {
8181

8282
// TODO [#1283]: Improve this error message. The vm should not be exposed and the message is not helpful.
8383
expect(() => document.body.appendChild(elm)).toThrowError(
84-
/Invalid key value "null" in \[object:vm undefined \(\d+\)\]. Key must be a string or number./
84+
/Invalid key value "null" in \[object:vm Test \(\d+\)\]. Key must be a string or number./
8585
);
8686
});

0 commit comments

Comments
 (0)