Skip to content

Commit 13fac03

Browse files
authored
fix(compiler): only create one class member when transforming @Element() decorators (#4547)
* only create one class member for an `@Element()` * add some regression tests
1 parent e11ac0e commit 13fac03

File tree

4 files changed

+100
-22
lines changed

4 files changed

+100
-22
lines changed

src/compiler/transformers/component-lazy/lazy-element-getter.ts

+29-14
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,35 @@ export const addLazyElementGetter = (
1414
if (cmp.elementRef) {
1515
addCoreRuntimeApi(moduleFile, RUNTIME_APIS.getElement);
1616

17-
classMembers.push(
18-
ts.factory.createGetAccessorDeclaration(
19-
undefined,
20-
cmp.elementRef,
21-
[],
22-
undefined,
23-
ts.factory.createBlock([
24-
ts.factory.createReturnStatement(
25-
ts.factory.createCallExpression(ts.factory.createIdentifier(GET_ELEMENT), undefined, [
26-
ts.factory.createThis(),
27-
])
28-
),
29-
])
30-
)
17+
// Create the getter that will be used in the transformed class declaration
18+
const getter = ts.factory.createGetAccessorDeclaration(
19+
undefined,
20+
cmp.elementRef,
21+
[],
22+
undefined,
23+
ts.factory.createBlock([
24+
ts.factory.createReturnStatement(
25+
ts.factory.createCallExpression(ts.factory.createIdentifier(GET_ELEMENT), undefined, [
26+
ts.factory.createThis(),
27+
])
28+
),
29+
])
3130
);
31+
32+
// Find the index in the class members array that correlates with the element
33+
// ref identifier we have
34+
const index = classMembers.findIndex(
35+
(member) =>
36+
member.kind === ts.SyntaxKind.PropertyDeclaration && (member.name as any)?.escapedText === cmp.elementRef
37+
);
38+
39+
// Index should never not be a valid integer, but we'll be safe just in case.
40+
// If the index is valid, we'll overwrite the existing class member with the getter
41+
// so we don't create multiple members with the same identifier
42+
if (index >= 0) {
43+
classMembers[index] = getter;
44+
} else {
45+
classMembers.push(getter);
46+
}
3247
}
3348
};

src/compiler/transformers/component-native/native-element-getter.ts

+24-8
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,30 @@ export const addNativeElementGetter = (classMembers: ts.ClassElement[], cmp: d.C
77
// is transformed into:
88
// get element() { return this; }
99
if (cmp.elementRef) {
10-
classMembers.push(
11-
ts.factory.createGetAccessorDeclaration(
12-
undefined,
13-
cmp.elementRef,
14-
[],
15-
undefined,
16-
ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createThis())])
17-
)
10+
// Create the getter that will be used in the transformed class declaration
11+
const getter = ts.factory.createGetAccessorDeclaration(
12+
undefined,
13+
cmp.elementRef,
14+
[],
15+
undefined,
16+
ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createThis())])
1817
);
18+
19+
ts.SyntaxKind.AmpersandToken;
20+
// Find the index in the class members array that correlates with the element
21+
// ref identifier we have
22+
const index = classMembers.findIndex(
23+
(member) =>
24+
member.kind === ts.SyntaxKind.PropertyDeclaration && (member.name as any)?.escapedText === cmp.elementRef
25+
);
26+
27+
// Index should never not be a valid integer, but we'll be safe just in case.
28+
// If the index is valid, we'll overwrite the existing class member with the getter
29+
// so we don't create multiple members with the same identifier
30+
if (index >= 0) {
31+
classMembers[index] = getter;
32+
} else {
33+
classMembers.push(getter);
34+
}
1935
}
2036
};

src/compiler/transformers/test/lazy-component.spec.ts

+29
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,33 @@ describe('lazy-component', () => {
3333
expect(t.outputText).toContain(`import { registerInstance as __stencil_registerInstance } from "@stencil/core"`);
3434
expect(t.outputText).toContain(`__stencil_registerInstance(this, hostRef)`);
3535
});
36+
37+
it('adds a getter for an @Element() reference', () => {
38+
const compilerCtx = mockCompilerCtx();
39+
const transformOpts: d.TransformOptions = {
40+
coreImportPath: '@stencil/core',
41+
componentExport: 'lazy',
42+
componentMetadata: null,
43+
currentDirectory: '/',
44+
proxy: null,
45+
style: 'static',
46+
styleImportData: null,
47+
};
48+
49+
const code = `
50+
@Component({
51+
tag: 'cmp-a'
52+
})
53+
export class CmpA {
54+
@Element() el: HtmlElement;
55+
}
56+
`;
57+
58+
const transformer = lazyComponentTransform(compilerCtx, transformOpts);
59+
60+
const t = transpileModule(code, null, compilerCtx, [], [transformer]);
61+
62+
expect(t.outputText).toContain(`get el() { return __stencil_getElement(this); } };`);
63+
expect(t.outputText).not.toContain(`el;`);
64+
});
3665
});

src/compiler/transformers/test/native-constructor.spec.ts

+18
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,23 @@ describe('nativeComponentTransform', () => {
6767
);
6868
expect(transpiledModule.outputText).toContain(`this.__attachShadow()`);
6969
});
70+
71+
it('adds a getter for an @Element() reference', () => {
72+
const code = `
73+
@Component({
74+
tag: 'cmp-a'
75+
})
76+
export class CmpA {
77+
@Element() el: HtmlElement;
78+
}
79+
`;
80+
81+
const transformer = nativeComponentTransform(compilerCtx, transformOpts);
82+
83+
const transpiledModule = transpileModule(code, null, compilerCtx, [], [transformer]);
84+
85+
expect(transpiledModule.outputText).toContain(`get el() { return this; }`);
86+
expect(transpiledModule.outputText).not.toContain(`el;`);
87+
});
7088
});
7189
});

0 commit comments

Comments
 (0)