@@ -38,8 +38,8 @@ export const propDecoratorsToStatic = (
38
38
decoratorName : string ,
39
39
) : void => {
40
40
const properties = decoratedProps
41
- . filter ( ts . isPropertyDeclaration )
42
- . map ( ( prop ) => parsePropDecorator ( diagnostics , typeChecker , program , prop , decoratorName ) )
41
+ . filter ( ( prop ) => ts . isPropertyDeclaration ( prop ) || ts . isGetAccessor ( prop ) )
42
+ . map ( ( prop ) => parsePropDecorator ( diagnostics , typeChecker , program , prop , decoratorName , newMembers ) )
43
43
. filter ( ( prop ) : prop is ts . PropertyAssignment => prop != null ) ;
44
44
45
45
if ( properties . length > 0 ) {
@@ -55,14 +55,16 @@ export const propDecoratorsToStatic = (
55
55
* @param program a {@link ts.Program} object
56
56
* @param prop the TypeScript `PropertyDeclaration` to parse
57
57
* @param decoratorName the name of the decorator to look for
58
+ * @param newMembers a collection of parsed `@Prop` annotated class members. Used for `get()` decorated props to find a corresponding `set()`
58
59
* @returns a property assignment expression to be added to the Stencil component's class
59
60
*/
60
61
const parsePropDecorator = (
61
62
diagnostics : d . Diagnostic [ ] ,
62
63
typeChecker : ts . TypeChecker ,
63
64
program : ts . Program ,
64
- prop : ts . PropertyDeclaration ,
65
+ prop : ts . PropertyDeclaration | ts . GetAccessorDeclaration ,
65
66
decoratorName : string ,
67
+ newMembers : ts . ClassElement [ ] ,
66
68
) : ts . PropertyAssignment | null => {
67
69
const propDecorator = retrieveTsDecorators ( prop ) ?. find ( isDecoratorNamed ( decoratorName ) ) ;
68
70
if ( propDecorator == null ) {
@@ -92,6 +94,7 @@ const parsePropDecorator = (
92
94
const symbol = typeChecker . getSymbolAtLocation ( prop . name ) ;
93
95
const type = typeChecker . getTypeAtLocation ( prop ) ;
94
96
const typeStr = propTypeFromTSType ( type ) ;
97
+ const foundSetter = ts . isGetAccessor ( prop ) ? findSetter ( propName , newMembers ) : null ;
95
98
96
99
const propMeta : d . ComponentCompilerStaticProperty = {
97
100
type : typeStr ,
@@ -100,6 +103,8 @@ const parsePropDecorator = (
100
103
required : prop . exclamationToken !== undefined && propName !== 'mode' ,
101
104
optional : prop . questionToken !== undefined ,
102
105
docs : serializeSymbol ( typeChecker , symbol ) ,
106
+ getter : ts . isGetAccessor ( prop ) ,
107
+ setter : ! ! foundSetter ,
103
108
} ;
104
109
105
110
// prop can have an attribute if type is NOT "unknown"
@@ -109,9 +114,30 @@ const parsePropDecorator = (
109
114
}
110
115
111
116
// extract default value
112
- const initializer = prop . initializer ;
113
- if ( initializer ) {
114
- propMeta . defaultValue = initializer . getText ( ) ;
117
+ if ( ts . isPropertyDeclaration ( prop ) && prop . initializer ) {
118
+ propMeta . defaultValue = prop . initializer . getText ( ) ;
119
+ } else if ( ts . isGetAccessorDeclaration ( prop ) ) {
120
+ // shallow comb to find default value for a getter
121
+ const returnStatement = prop . body ?. statements . find ( ( st ) => ts . isReturnStatement ( st ) ) as ts . ReturnStatement ;
122
+ const returnExpression = returnStatement . expression ;
123
+
124
+ if ( returnExpression && ts . isLiteralExpression ( returnExpression ) ) {
125
+ // the getter has a literal return value
126
+ propMeta . defaultValue = returnExpression . getText ( ) ;
127
+ } else if ( returnExpression && ts . isPropertyAccessExpression ( returnExpression ) ) {
128
+ const nameToFind = returnExpression . name . getText ( ) ;
129
+ const foundProp = findGetProp ( nameToFind , newMembers ) ;
130
+
131
+ if ( foundProp && foundProp . initializer ) {
132
+ propMeta . defaultValue = foundProp . initializer . getText ( ) ;
133
+
134
+ if ( propMeta . type === 'unknown' ) {
135
+ const type = typeChecker . getTypeAtLocation ( foundProp ) ;
136
+ propMeta . type = propTypeFromTSType ( type ) ;
137
+ propMeta . complexType = getComplexType ( typeChecker , foundProp , type , program ) ;
138
+ }
139
+ }
140
+ }
115
141
}
116
142
117
143
const staticProp = ts . factory . createPropertyAssignment (
@@ -164,7 +190,7 @@ const getReflect = (diagnostics: d.Diagnostic[], propDecorator: ts.Decorator, pr
164
190
165
191
const getComplexType = (
166
192
typeChecker : ts . TypeChecker ,
167
- node : ts . PropertyDeclaration ,
193
+ node : ts . PropertyDeclaration | ts . GetAccessorDeclaration ,
168
194
type : ts . Type ,
169
195
program : ts . Program ,
170
196
) : d . ComponentCompilerPropertyComplexType => {
@@ -293,3 +319,26 @@ const isAny = (t: ts.Type): boolean => {
293
319
}
294
320
return false ;
295
321
} ;
322
+
323
+ /**
324
+ * Attempts to find a `set` member of the class when there is a corresponding getter
325
+ * @param propName - the property name of the setter to find
326
+ * @param members - all the component class members
327
+ * @returns the found typescript AST setter node
328
+ */
329
+ const findSetter = ( propName : string , members : ts . ClassElement [ ] ) : ts . SetAccessorDeclaration | undefined => {
330
+ return members . find ( ( m ) => ts . isSetAccessor ( m ) && m . name . getText ( ) === propName ) as
331
+ | ts . SetAccessorDeclaration
332
+ | undefined ;
333
+ } ;
334
+
335
+ /**
336
+ * When attempting to find the default value of a decorated `get` prop, if a member like `this.something`
337
+ * is returned, this method is used to comb the class members to attempt to get it's default value
338
+ * @param propName - the property name of the member to find
339
+ * @param members - all the component class members
340
+ * @returns the found typescript AST class member
341
+ */
342
+ const findGetProp = ( propName : string , members : ts . ClassElement [ ] ) : ts . PropertyDeclaration | undefined => {
343
+ return members . find ( ( m ) => ts . isPropertyDeclaration ( m ) && m . name . getText ( ) === propName ) as ts . PropertyDeclaration ;
344
+ } ;
0 commit comments