1
- import { CallExpression , Expression , Node , ObjectLiteralExpression } from "typescript" ;
1
+ import { SimpleType } from "ts-simple-type" ;
2
+ import * as tsModule from "typescript" ;
3
+ import { CallExpression , Node , PropertyAssignment } from "typescript" ;
2
4
import { AnalyzerVisitContext } from "../../analyzer-visit-context" ;
3
5
import { LitElementPropertyConfig } from "../../types/features/lit-element-property-config" ;
4
6
import { resolveNodeValue } from "../../util/resolve-node-value" ;
@@ -40,8 +42,6 @@ export function getLitElementPropertyDecorator(
40
42
* @param context
41
43
*/
42
44
export function getLitElementPropertyDecoratorConfig ( node : Node , context : AnalyzerVisitContext ) : undefined | LitElementPropertyConfig {
43
- const { ts } = context ;
44
-
45
45
// Get reference to a possible "@property" decorator.
46
46
const decorator = getLitElementPropertyDecorator ( node , context ) ;
47
47
@@ -61,124 +61,112 @@ export function getLitElementPropertyDecoratorConfig(node: Node, context: Analyz
61
61
break ;
62
62
}
63
63
64
- // Get lit options from the object literal expression
65
- return configNode != null && ts . isObjectLiteralExpression ( configNode ) ? getLitPropertyOptions ( configNode , context , config ) : config ;
64
+ if ( configNode == null ) {
65
+ return config ;
66
+ }
67
+
68
+ const resolved = resolveNodeValue ( configNode , context ) ;
69
+
70
+ return resolved != null ? getLitPropertyOptions ( resolved . node , resolved . value , context , config ) : config ;
66
71
}
67
72
68
73
return undefined ;
69
74
}
70
75
76
+ function hasProperty < T extends string > ( obj : object , key : T ) : obj is { [ K in T ] : unknown } {
77
+ return Object . prototype . hasOwnProperty . call ( obj , key ) ;
78
+ }
79
+
80
+ export function getLitPropertyType ( ts : typeof tsModule , node : Node ) : SimpleType | string {
81
+ const value = ts . isIdentifier ( node ) ? node . text : undefined ;
82
+
83
+ switch ( value ) {
84
+ case "String" :
85
+ case "StringConstructor" :
86
+ return { kind : "STRING" } ;
87
+ case "Number" :
88
+ case "NumberConstructor" :
89
+ return { kind : "NUMBER" } ;
90
+ case "Boolean" :
91
+ case "BooleanConstructor" :
92
+ return { kind : "BOOLEAN" } ;
93
+ case "Array" :
94
+ case "ArrayConstructor" :
95
+ return { kind : "ARRAY" , type : { kind : "ANY" } } ;
96
+ case "Object" :
97
+ case "ObjectConstructor" :
98
+ return { kind : "OBJECT" , members : [ ] } ;
99
+ default :
100
+ // This is an unknown type, so set the name as a string
101
+ return node . getText ( ) ;
102
+ }
103
+ }
104
+
71
105
/**
72
106
* Parses an object literal expression and returns a lit property configuration.
73
107
* @param node
74
108
* @param existingConfig
75
109
* @param context
76
110
*/
77
111
export function getLitPropertyOptions (
78
- node : ObjectLiteralExpression ,
112
+ node : Node ,
113
+ object : unknown ,
79
114
context : AnalyzerVisitContext ,
80
115
existingConfig : LitElementPropertyConfig = { }
81
116
) : LitElementPropertyConfig {
82
117
const { ts } = context ;
118
+ const result : LitElementPropertyConfig = { ...existingConfig } ;
119
+ let attributeInitializer : Node | undefined ;
120
+ let typeInitializer : Node | undefined ;
83
121
84
- // Build up the property configuration by looking at properties in the object literal expression
85
- return node . properties . reduce ( ( config , property ) => {
86
- if ( ! ts . isPropertyAssignment ( property ) ) return config ;
87
-
88
- const initializer = property . initializer ;
89
- const kind = ts . isIdentifier ( property . name ) ? property . name . text : undefined ;
90
-
91
- return parseLitPropertyOption ( { kind, initializer, config } , context ) ;
92
- } , existingConfig ) ;
93
- }
94
-
95
- export function parseLitPropertyOption (
96
- { kind, initializer, config } : { kind : string | undefined ; initializer : Expression ; config : LitElementPropertyConfig } ,
97
- context : AnalyzerVisitContext
98
- ) : LitElementPropertyConfig {
99
- const { ts, checker } = context ;
100
-
101
- // noinspection DuplicateCaseLabelJS
102
- switch ( kind ) {
103
- case "converter" : {
104
- return { ...config , hasConverter : true } ;
122
+ if ( typeof object === "object" && object !== null && ! Array . isArray ( object ) ) {
123
+ if ( hasProperty ( object , "converter" ) && object . converter !== undefined ) {
124
+ result . hasConverter = true ;
105
125
}
106
126
107
- case "reflect" : {
108
- return { ... config , reflect : resolveNodeValue ( initializer , context ) ?. value === true } ;
127
+ if ( hasProperty ( object , "reflect" ) && object . reflect !== undefined ) {
128
+ result . reflect = object . reflect === true ;
109
129
}
110
130
111
- case "state" : {
112
- return { ... config , state : resolveNodeValue ( initializer , context ) ?. value === true } ;
131
+ if ( hasProperty ( object , "state" ) && object . state !== undefined ) {
132
+ result . state = object . state === true ;
113
133
}
114
134
115
- case "attribute" : {
116
- let attribute : LitElementPropertyConfig [ "attribute" ] | undefined ;
117
-
118
- if ( initializer . kind === ts . SyntaxKind . TrueKeyword ) {
119
- attribute = true ;
120
- } else if ( initializer . kind === ts . SyntaxKind . FalseKeyword ) {
121
- attribute = false ;
122
- } else if ( ts . isStringLiteral ( initializer ) ) {
123
- attribute = initializer . text ;
124
- }
125
-
126
- return {
127
- ...config ,
128
- attribute,
129
- node : {
130
- ...( config . node || { } ) ,
131
- attribute : initializer
132
- }
133
- } ;
135
+ if ( hasProperty ( object , "value" ) ) {
136
+ result . default = object . value ;
134
137
}
135
138
136
- case "type" : {
137
- let type : LitElementPropertyConfig [ "type" ] | undefined ;
138
- const value = ts . isIdentifier ( initializer ) ? initializer . text : undefined ;
139
-
140
- switch ( value ) {
141
- case "String" :
142
- case "StringConstructor" :
143
- type = { kind : "STRING" } ;
144
- break ;
145
- case "Number" :
146
- case "NumberConstructor" :
147
- type = { kind : "NUMBER" } ;
148
- break ;
149
- case "Boolean" :
150
- case "BooleanConstructor" :
151
- type = { kind : "BOOLEAN" } ;
152
- break ;
153
- case "Array" :
154
- case "ArrayConstructor" :
155
- type = { kind : "ARRAY" , type : { kind : "ANY" } } ;
156
- break ;
157
- case "Object" :
158
- case "ObjectConstructor" :
159
- type = { kind : "OBJECT" , members : [ ] } ;
160
- break ;
161
- default :
162
- // This is an unknown type, so set the name as a string
163
- type = initializer . getText ( ) ;
164
- break ;
165
- }
139
+ if ( hasProperty ( object , "attribute" ) && ( typeof object . attribute === "boolean" || typeof object . attribute === "string" ) ) {
140
+ result . attribute = object . attribute ;
166
141
167
- return {
168
- ... config ,
169
- type ,
170
- node : {
171
- ... ( config . node || { } ) ,
172
- type : initializer
142
+ if ( ts . isObjectLiteralExpression ( node ) ) {
143
+ const prop = node . properties . find (
144
+ ( p ) : p is PropertyAssignment => ts . isPropertyAssignment ( p ) && ts . isIdentifier ( p . name ) && p . name . text === "attribute"
145
+ ) ;
146
+ if ( prop ) {
147
+ attributeInitializer = prop . initializer ;
173
148
}
174
- } ;
149
+ }
175
150
}
151
+ }
152
+
153
+ if ( ts . isObjectLiteralExpression ( node ) ) {
154
+ const typeProp = node . properties . find (
155
+ ( p ) : p is PropertyAssignment => ts . isPropertyAssignment ( p ) && ts . isIdentifier ( p . name ) && p . name . text === "type"
156
+ ) ;
176
157
177
- // Polymer specific field
178
- case "value" : {
179
- return { ... config , default : resolveNodeValue ( initializer , { ts, checker } ) ?. value } ;
158
+ if ( typeProp ) {
159
+ typeInitializer = typeProp . initializer ;
160
+ result . type = getLitPropertyType ( ts , typeProp . initializer ) ;
180
161
}
181
162
}
182
163
183
- return config ;
164
+ return {
165
+ ...result ,
166
+ node : {
167
+ ...( result . node || { } ) ,
168
+ attribute : attributeInitializer ,
169
+ type : typeInitializer
170
+ }
171
+ } ;
184
172
}
0 commit comments