| 
									
										
										
											
												feat(core): add dynamic queries schematic (#32231)
Adds a schematic that will remove the explicit `static: false` flag from dynamic queries. E.g.
```ts
import { Directive, ViewChild, ContentChild, ElementRef } from '@angular/core';
@Directive()
export class MyDirective {
  @ViewChild('child', { static: false }) child: any;
  @ViewChild('secondChild', { read: ElementRef, static: false }) secondChild: ElementRef;
  @ContentChild('thirdChild', { static: false }) thirdChild: any;
}
```
```ts
import { Directive, ViewChild, ContentChild, ElementRef } from '@angular/core';
@Directive()
export class MyDirective {
  @ViewChild('child') child: any;
  @ViewChild('secondChild', { read: ElementRef }) secondChild: ElementRef;
  @ContentChild('thirdChild') thirdChild: any;
}
```
PR Close #32231
											
										 
											2019-08-21 07:40:30 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
											
												feat(core): add dynamic queries schematic (#32231)
Adds a schematic that will remove the explicit `static: false` flag from dynamic queries. E.g.
```ts
import { Directive, ViewChild, ContentChild, ElementRef } from '@angular/core';
@Directive()
export class MyDirective {
  @ViewChild('child', { static: false }) child: any;
  @ViewChild('secondChild', { read: ElementRef, static: false }) secondChild: ElementRef;
  @ContentChild('thirdChild', { static: false }) thirdChild: any;
}
```
```ts
import { Directive, ViewChild, ContentChild, ElementRef } from '@angular/core';
@Directive()
export class MyDirective {
  @ViewChild('child') child: any;
  @ViewChild('secondChild', { read: ElementRef }) secondChild: ElementRef;
  @ContentChild('thirdChild') thirdChild: any;
}
```
PR Close #32231
											
										 
											2019-08-21 07:40:30 +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'; | 
					
						
							|  |  |  | import {getAngularDecorators} from '../../utils/ng_decorators'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Identifies the nodes that should be migrated by the dynamic | 
					
						
							|  |  |  |  * queries schematic. Splits the nodes into the following categories: | 
					
						
							|  |  |  |  * - `removeProperty` - queries from which we should only remove the `static` property of the | 
					
						
							|  |  |  |  *  `options` parameter (e.g. `@ViewChild('child', {static: false, read: ElementRef})`). | 
					
						
							|  |  |  |  * - `removeParameter` - queries from which we should drop the entire `options` parameter. | 
					
						
							|  |  |  |  *  (e.g. `@ViewChild('child', {static: false})`). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function identifyDynamicQueryNodes(typeChecker: ts.TypeChecker, sourceFile: ts.SourceFile) { | 
					
						
							|  |  |  |   const removeProperty: ts.ObjectLiteralExpression[] = []; | 
					
						
							|  |  |  |   const removeParameter: ts.CallExpression[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sourceFile.forEachChild(function walk(node: ts.Node) { | 
					
						
							|  |  |  |     if (ts.isClassDeclaration(node)) { | 
					
						
							|  |  |  |       node.members.forEach(member => { | 
					
						
							|  |  |  |         const angularDecorators = | 
					
						
							|  |  |  |             member.decorators && getAngularDecorators(typeChecker, member.decorators); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (angularDecorators) { | 
					
						
							|  |  |  |           angularDecorators | 
					
						
							|  |  |  |               // Filter out the queries that can have the `static` flag.
 | 
					
						
							|  |  |  |               .filter(decorator => { | 
					
						
							|  |  |  |                 return decorator.name === 'ViewChild' || decorator.name === 'ContentChild'; | 
					
						
							|  |  |  |               }) | 
					
						
							|  |  |  |               // Filter out the queries where the `static` flag is explicitly set to `false`.
 | 
					
						
							|  |  |  |               .filter(decorator => { | 
					
						
							|  |  |  |                 const options = decorator.node.expression.arguments[1]; | 
					
						
							|  |  |  |                 return options && ts.isObjectLiteralExpression(options) && | 
					
						
							|  |  |  |                     options.properties.some( | 
					
						
							|  |  |  |                         property => ts.isPropertyAssignment(property) && | 
					
						
							|  |  |  |                             property.initializer.kind === ts.SyntaxKind.FalseKeyword); | 
					
						
							|  |  |  |               }) | 
					
						
							|  |  |  |               .forEach(decorator => { | 
					
						
							|  |  |  |                 const options = | 
					
						
							|  |  |  |                     decorator.node.expression.arguments[1] as ts.ObjectLiteralExpression; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // At this point we know that at least one property is the `static` flag. If this is
 | 
					
						
							|  |  |  |                 // the only property we can drop the entire object literal, otherwise we have to
 | 
					
						
							|  |  |  |                 // drop only the property.
 | 
					
						
							|  |  |  |                 if (options.properties.length === 1) { | 
					
						
							|  |  |  |                   removeParameter.push(decorator.node.expression); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                   removeProperty.push(options); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     node.forEachChild(walk); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return {removeProperty, removeParameter}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Removes the `options` parameter from the call expression of a query decorator. */ | 
					
						
							|  |  |  | export function removeOptionsParameter(node: ts.CallExpression): ts.CallExpression { | 
					
						
							|  |  |  |   return ts.updateCall(node, node.expression, node.typeArguments, [node.arguments[0]]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Removes the `static` property from an object literal expression. */ | 
					
						
							|  |  |  | export function removeStaticFlag(node: ts.ObjectLiteralExpression): ts.ObjectLiteralExpression { | 
					
						
							|  |  |  |   return ts.updateObjectLiteral( | 
					
						
							|  |  |  |       node, | 
					
						
							|  |  |  |       node.properties.filter(property => property.name && property.name.getText() !== 'static')); | 
					
						
							|  |  |  | } |