| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import * as ts from 'typescript'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-12 14:33:06 +02:00
										 |  |  | import {getImportSpecifier} from '../../utils/typescript/imports'; | 
					
						
							|  |  |  | import {isReferenceToImport} from '../../utils/typescript/symbol'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Finds typed nodes (e.g. function parameters or class properties) that are referencing the old | 
					
						
							|  |  |  |  * `Renderer`, as well as calls to the `Renderer` methods. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function findRendererReferences( | 
					
						
							| 
									
										
										
										
											2020-09-12 14:33:06 +02:00
										 |  |  |     sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker, | 
					
						
							|  |  |  |     rendererImportSpecifier: ts.ImportSpecifier) { | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  |   const typedNodes = new Set<ts.ParameterDeclaration|ts.PropertyDeclaration|ts.AsExpression>(); | 
					
						
							|  |  |  |   const methodCalls = new Set<ts.CallExpression>(); | 
					
						
							|  |  |  |   const forwardRefs = new Set<ts.Identifier>(); | 
					
						
							| 
									
										
										
										
											2020-09-12 14:33:06 +02:00
										 |  |  |   const forwardRefSpecifier = getImportSpecifier(sourceFile, '@angular/core', 'forwardRef'); | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   ts.forEachChild(sourceFile, function visitNode(node: ts.Node) { | 
					
						
							|  |  |  |     if ((ts.isParameter(node) || ts.isPropertyDeclaration(node)) && | 
					
						
							| 
									
										
										
										
											2020-09-12 14:33:06 +02:00
										 |  |  |         isReferenceToImport(typeChecker, node.name, rendererImportSpecifier)) { | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  |       typedNodes.add(node); | 
					
						
							|  |  |  |     } else if ( | 
					
						
							| 
									
										
										
										
											2020-09-12 14:33:06 +02:00
										 |  |  |         ts.isAsExpression(node) && | 
					
						
							|  |  |  |         isReferenceToImport(typeChecker, node.type, rendererImportSpecifier)) { | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  |       typedNodes.add(node); | 
					
						
							|  |  |  |     } else if (ts.isCallExpression(node)) { | 
					
						
							|  |  |  |       if (ts.isPropertyAccessExpression(node.expression) && | 
					
						
							| 
									
										
										
										
											2020-09-12 14:33:06 +02:00
										 |  |  |           isReferenceToImport(typeChecker, node.expression.expression, rendererImportSpecifier)) { | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  |         methodCalls.add(node); | 
					
						
							|  |  |  |       } else if ( | 
					
						
							|  |  |  |           // If we're dealing with a forwardRef that's returning a Renderer.
 | 
					
						
							|  |  |  |           forwardRefSpecifier && ts.isIdentifier(node.expression) && | 
					
						
							|  |  |  |           isReferenceToImport(typeChecker, node.expression, forwardRefSpecifier) && | 
					
						
							|  |  |  |           node.arguments.length) { | 
					
						
							|  |  |  |         const rendererIdentifier = | 
					
						
							| 
									
										
										
										
											2020-09-12 14:33:06 +02:00
										 |  |  |             findRendererIdentifierInForwardRef(typeChecker, node, rendererImportSpecifier); | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  |         if (rendererIdentifier) { | 
					
						
							|  |  |  |           forwardRefs.add(rendererIdentifier); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ts.forEachChild(node, visitNode); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return {typedNodes, methodCalls, forwardRefs}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Finds the identifier referring to the `Renderer` inside a `forwardRef` call expression. */ | 
					
						
							|  |  |  | function findRendererIdentifierInForwardRef( | 
					
						
							|  |  |  |     typeChecker: ts.TypeChecker, node: ts.CallExpression, | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |     rendererImport: ts.ImportSpecifier|null): ts.Identifier|null { | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  |   const firstArg = node.arguments[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-12 14:33:06 +02:00
										 |  |  |   if (ts.isArrowFunction(firstArg) && rendererImport) { | 
					
						
							| 
									
										
										
										
											2019-06-09 15:38:18 +02:00
										 |  |  |     // Check if the function is `forwardRef(() => Renderer)`.
 | 
					
						
							|  |  |  |     if (ts.isIdentifier(firstArg.body) && | 
					
						
							|  |  |  |         isReferenceToImport(typeChecker, firstArg.body, rendererImport)) { | 
					
						
							|  |  |  |       return firstArg.body; | 
					
						
							|  |  |  |     } else if (ts.isBlock(firstArg.body) && ts.isReturnStatement(firstArg.body.statements[0])) { | 
					
						
							|  |  |  |       // Otherwise check if the expression is `forwardRef(() => { return Renderer })`.
 | 
					
						
							|  |  |  |       const returnStatement = firstArg.body.statements[0] as ts.ReturnStatement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (returnStatement.expression && ts.isIdentifier(returnStatement.expression) && | 
					
						
							|  |  |  |           isReferenceToImport(typeChecker, returnStatement.expression, rendererImport)) { | 
					
						
							|  |  |  |         return returnStatement.expression; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return null; | 
					
						
							|  |  |  | } |