@@ -103,21 +103,129 @@ export function resolveSource(obj: NodePath): {
103103 }
104104
105105 const path = resolve ( obj ) ;
106- switch ( path ?. type ) {
106+ if ( ! path ) return { id : null , placement : null } ;
107+
108+ switch ( path . type ) {
109+ case "NullLiteral" :
110+ return { id : null , placement : null } ;
107111 case "RegExpLiteral" :
108112 return { id : "RegExp" , placement : "prototype" } ;
109- case "FunctionExpression" :
110- return { id : "Function" , placement : "prototype" } ;
111113 case "StringLiteral" :
114+ case "TemplateLiteral" :
112115 return { id : "String" , placement : "prototype" } ;
113- case "NumberLiteral " :
116+ case "NumericLiteral " :
114117 return { id : "Number" , placement : "prototype" } ;
115118 case "BooleanLiteral" :
116119 return { id : "Boolean" , placement : "prototype" } ;
120+ case "BigIntLiteral" :
121+ return { id : "BigInt" , placement : "prototype" } ;
117122 case "ObjectExpression" :
118123 return { id : "Object" , placement : "prototype" } ;
119124 case "ArrayExpression" :
120125 return { id : "Array" , placement : "prototype" } ;
126+ case "FunctionExpression" :
127+ case "ArrowFunctionExpression" :
128+ case "ClassExpression" :
129+ return { id : "Function" , placement : "prototype" } ;
130+ // new Constructor() -> resolve the constructor name
131+ case "NewExpression" : {
132+ const calleeId = resolveId (
133+ ( path as NodePath < t . NewExpression > ) . get ( "callee" ) ,
134+ ) ;
135+ if ( calleeId ) return { id : calleeId , placement : "prototype" } ;
136+ return { id : null , placement : null } ;
137+ }
138+ // Unary expressions -> result type depends on operator
139+ case "UnaryExpression" : {
140+ const { operator } = path . node as t . UnaryExpression ;
141+ if ( operator === "typeof" )
142+ return { id : "String" , placement : "prototype" } ;
143+ if ( operator === "!" || operator === "delete" )
144+ return { id : "Boolean" , placement : "prototype" } ;
145+ if ( operator === "+" || operator === "-" || operator === "~" )
146+ return { id : "Number" , placement : "prototype" } ;
147+ return { id : null , placement : null } ;
148+ }
149+ // ++i, i++ always produce a number
150+ case "UpdateExpression" :
151+ return { id : "Number" , placement : "prototype" } ;
152+ // Binary expressions -> result type depends on operator
153+ case "BinaryExpression" : {
154+ const { operator } = path . node as t . BinaryExpression ;
155+ if (
156+ operator === "==" ||
157+ operator === "!=" ||
158+ operator === "===" ||
159+ operator === "!==" ||
160+ operator === "<" ||
161+ operator === ">" ||
162+ operator === "<=" ||
163+ operator === ">=" ||
164+ operator === "instanceof" ||
165+ operator === "in"
166+ ) {
167+ return { id : "Boolean" , placement : "prototype" } ;
168+ }
169+ if (
170+ operator === "-" ||
171+ operator === "*" ||
172+ operator === "/" ||
173+ operator === "%" ||
174+ operator === "**" ||
175+ operator === "&" ||
176+ operator === "|" ||
177+ operator === "^" ||
178+ operator === "<<" ||
179+ operator === ">>" ||
180+ operator === ">>>"
181+ ) {
182+ return { id : "Number" , placement : "prototype" } ;
183+ }
184+ // + is ambiguous (string or number), so we can't determine the type
185+ return { id : null , placement : null } ;
186+ }
187+ // (a, b, c) -> the result is the last expression
188+ case "SequenceExpression" : {
189+ const expressions = ( path as NodePath < t . SequenceExpression > ) . get (
190+ "expressions" ,
191+ ) ;
192+ return resolveSource ( expressions [ expressions . length - 1 ] ) ;
193+ }
194+ // a = b -> the result is the right side
195+ case "AssignmentExpression" : {
196+ if ( ( path . node as t . AssignmentExpression ) . operator === "=" ) {
197+ return resolveSource (
198+ ( path as NodePath < t . AssignmentExpression > ) . get ( "right" ) ,
199+ ) ;
200+ }
201+ return { id : null , placement : null } ;
202+ }
203+ // a ? b : c -> if both branches resolve to the same type, use it
204+ case "ConditionalExpression" : {
205+ const consequent = resolveSource (
206+ ( path as NodePath < t . ConditionalExpression > ) . get ( "consequent" ) ,
207+ ) ;
208+ const alternate = resolveSource (
209+ ( path as NodePath < t . ConditionalExpression > ) . get ( "alternate" ) ,
210+ ) ;
211+ if ( consequent . id && consequent . id === alternate . id ) {
212+ return consequent ;
213+ }
214+ return { id : null , placement : null } ;
215+ }
216+ // (expr) -> unwrap parenthesized expressions
217+ case "ParenthesizedExpression" :
218+ return resolveSource (
219+ ( path as NodePath < t . ParenthesizedExpression > ) . get ( "expression" ) ,
220+ ) ;
221+ // TypeScript / Flow type wrappers -> unwrap to the inner expression
222+ case "TSAsExpression" :
223+ case "TSSatisfiesExpression" :
224+ case "TSNonNullExpression" :
225+ case "TSInstantiationExpression" :
226+ case "TSTypeAssertion" :
227+ case "TypeCastExpression" :
228+ return resolveSource ( path . get ( "expression" ) as NodePath ) ;
121229 }
122230
123231 return { id : null , placement : null } ;
0 commit comments