| 
									
										
										
										
											2019-02-27 20:30:38 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2019-02-27 20:30:38 +01: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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 11:02:37 +02:00
										 |  |  | import {logging} from '@angular-devkit/core'; | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  | import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; | 
					
						
							| 
									
										
										
										
											2020-04-02 11:01:43 +02:00
										 |  |  | import {relative} from 'path'; | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  | import {from} from 'rxjs'; | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | import * as ts from 'typescript'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 16:16:56 +02:00
										 |  |  | import {NgComponentTemplateVisitor} from '../../utils/ng_component_template'; | 
					
						
							| 
									
										
										
										
											2019-02-27 20:30:38 +01:00
										 |  |  | import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; | 
					
						
							| 
									
										
										
										
											2020-04-02 11:01:43 +02:00
										 |  |  | import {createMigrationProgram} from '../../utils/typescript/compiler_host'; | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import {NgQueryResolveVisitor} from './angular/ng_query_visitor'; | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  | import {QueryTemplateStrategy} from './strategies/template_strategy/template_strategy'; | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  | import {QueryTestStrategy} from './strategies/test_strategy/test_strategy'; | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  | import {TimingStrategy} from './strategies/timing-strategy'; | 
					
						
							| 
									
										
										
										
											2019-04-08 19:37:31 +02:00
										 |  |  | import {QueryUsageStrategy} from './strategies/usage_strategy/usage_strategy'; | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | import {getTransformedQueryCallExpr} from './transform'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 00:55:18 +02:00
										 |  |  | enum SELECTED_STRATEGY { | 
					
						
							|  |  |  |   TEMPLATE, | 
					
						
							|  |  |  |   USAGE, | 
					
						
							|  |  |  |   TESTS, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  | interface AnalyzedProject { | 
					
						
							|  |  |  |   program: ts.Program; | 
					
						
							|  |  |  |   host: ts.CompilerHost; | 
					
						
							|  |  |  |   queryVisitor: NgQueryResolveVisitor; | 
					
						
							|  |  |  |   sourceFiles: ts.SourceFile[]; | 
					
						
							|  |  |  |   basePath: string; | 
					
						
							|  |  |  |   typeChecker: ts.TypeChecker; | 
					
						
							|  |  |  |   tsconfigPath: string; | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 20:30:38 +01:00
										 |  |  | /** Entry point for the V8 static-query migration. */ | 
					
						
							|  |  |  | export default function(): Rule { | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |   return (tree: Tree, context: SchematicContext) => { | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |     // We need to cast the returned "Observable" to "any" as there is a
 | 
					
						
							|  |  |  |     // RxJS version mismatch that breaks the TS compilation.
 | 
					
						
							|  |  |  |     return from(runMigration(tree, context).then(() => tree)) as any; | 
					
						
							| 
									
										
										
										
											2019-02-27 20:30:38 +01:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  | /** Runs the V8 migration static-query migration for all determined TypeScript projects. */ | 
					
						
							|  |  |  | async function runMigration(tree: Tree, context: SchematicContext) { | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  |   const {buildPaths, testPaths} = getProjectTsConfigPaths(tree); | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |   const basePath = process.cwd(); | 
					
						
							|  |  |  |   const logger = context.logger; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  |   if (!buildPaths.length && !testPaths.length) { | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |     throw new SchematicsException( | 
					
						
							|  |  |  |         'Could not find any tsconfig file. Cannot migrate queries ' + | 
					
						
							| 
									
										
										
										
											2019-05-23 16:28:57 -07:00
										 |  |  |         'to add static flag.'); | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 20:35:27 +02:00
										 |  |  |   const analyzedFiles = new Set<string>(); | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |   const buildProjects = new Set<AnalyzedProject>(); | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |   const failures = []; | 
					
						
							| 
									
										
										
										
											2019-05-23 00:55:18 +02:00
										 |  |  |   const strategy = process.env['NG_STATIC_QUERY_USAGE_STRATEGY'] === 'true' ? | 
					
						
							|  |  |  |       SELECTED_STRATEGY.USAGE : | 
					
						
							|  |  |  |       SELECTED_STRATEGY.TEMPLATE; | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   for (const tsconfigPath of buildPaths) { | 
					
						
							| 
									
										
										
										
											2019-05-23 02:56:27 +02:00
										 |  |  |     const project = analyzeProject(tree, tsconfigPath, basePath, analyzedFiles, logger); | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |     if (project) { | 
					
						
							|  |  |  |       buildProjects.add(project); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (buildProjects.size) { | 
					
						
							|  |  |  |     for (let project of Array.from(buildProjects.values())) { | 
					
						
							| 
									
										
										
										
											2019-05-08 11:02:37 +02:00
										 |  |  |       failures.push(...await runStaticQueryMigration(tree, project, strategy, logger)); | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  |   // For the "test" tsconfig projects we always want to use the test strategy as
 | 
					
						
							|  |  |  |   // we can't detect the proper timing within spec files.
 | 
					
						
							|  |  |  |   for (const tsconfigPath of testPaths) { | 
					
						
							| 
									
										
										
										
											2019-05-23 02:56:27 +02:00
										 |  |  |     const project = await analyzeProject(tree, tsconfigPath, basePath, analyzedFiles, logger); | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |     if (project) { | 
					
						
							| 
									
										
										
										
											2019-05-08 11:02:37 +02:00
										 |  |  |       failures.push( | 
					
						
							|  |  |  |           ...await runStaticQueryMigration(tree, project, SELECTED_STRATEGY.TESTS, logger)); | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (failures.length) { | 
					
						
							| 
									
										
										
										
											2019-05-14 20:59:21 +02:00
										 |  |  |     logger.info(''); | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |     logger.info('Some queries could not be migrated automatically. Please go'); | 
					
						
							| 
									
										
										
										
											2019-05-23 16:28:57 -07:00
										 |  |  |     logger.info('through these manually and apply the appropriate timing.'); | 
					
						
							|  |  |  |     logger.info('For more info on how to choose a flag, please see: '); | 
					
						
							| 
									
										
										
										
											2019-05-24 12:19:37 +02:00
										 |  |  |     logger.info('https://v8.angular.io/guide/static-query-migration'); | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |     failures.forEach(failure => logger.warn(`⮑   ${failure}`)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |  * Analyzes the given TypeScript project by looking for queries that need to be | 
					
						
							|  |  |  |  * migrated. In case there are no queries that can be migrated, null is returned. | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-05-14 20:35:27 +02:00
										 |  |  | function analyzeProject( | 
					
						
							| 
									
										
										
										
											2019-05-23 02:56:27 +02:00
										 |  |  |     tree: Tree, tsconfigPath: string, basePath: string, analyzedFiles: Set<string>, | 
					
						
							| 
									
										
										
										
											2020-04-02 11:01:43 +02:00
										 |  |  |     logger: logging.LoggerApi): AnalyzedProject|null { | 
					
						
							|  |  |  |   const {program, host} = createMigrationProgram(tree, tsconfigPath, basePath); | 
					
						
							|  |  |  |   const syntacticDiagnostics = program.getSyntacticDiagnostics(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Syntactic TypeScript errors can throw off the query analysis and therefore we want
 | 
					
						
							|  |  |  |   // to notify the developer that we couldn't analyze parts of the project. Developers
 | 
					
						
							|  |  |  |   // can just re-run the migration after fixing these failures.
 | 
					
						
							|  |  |  |   if (syntacticDiagnostics.length) { | 
					
						
							|  |  |  |     logger.warn( | 
					
						
							|  |  |  |         `\nTypeScript project "${tsconfigPath}" has syntactical errors which could cause ` + | 
					
						
							|  |  |  |         `an incomplete migration. Please fix the following failures and rerun the migration:`); | 
					
						
							|  |  |  |     logger.error(ts.formatDiagnostics(syntacticDiagnostics, host)); | 
					
						
							|  |  |  |     logger.info( | 
					
						
							|  |  |  |         'Migration can be rerun with: "ng update @angular/core --from 7 --to 8 --migrate-only"\n'); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 11:01:43 +02:00
										 |  |  |   const typeChecker = program.getTypeChecker(); | 
					
						
							|  |  |  |   const sourceFiles = program.getSourceFiles().filter( | 
					
						
							|  |  |  |       f => !f.isDeclarationFile && !program.isSourceFileFromExternalLibrary(f)); | 
					
						
							|  |  |  |   const queryVisitor = new NgQueryResolveVisitor(typeChecker); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Analyze all project source-files and collect all queries that
 | 
					
						
							|  |  |  |   // need to be migrated.
 | 
					
						
							|  |  |  |   sourceFiles.forEach(sourceFile => { | 
					
						
							|  |  |  |     const relativePath = relative(basePath, sourceFile.fileName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Only look for queries within the current source files if the
 | 
					
						
							|  |  |  |     // file has not been analyzed before.
 | 
					
						
							|  |  |  |     if (!analyzedFiles.has(relativePath)) { | 
					
						
							|  |  |  |       analyzedFiles.add(relativePath); | 
					
						
							|  |  |  |       queryVisitor.visitNode(sourceFile); | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-02 11:01:43 +02:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (queryVisitor.resolvedQueries.size === 0) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return {program, host, tsconfigPath, typeChecker, basePath, queryVisitor, sourceFiles}; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Runs the static query migration for the given project. The schematic analyzes all | 
					
						
							|  |  |  |  * queries within the project and sets up the query timing based on the current usage | 
					
						
							|  |  |  |  * of the query property. e.g. a view query that is not used in any lifecycle hook does | 
					
						
							|  |  |  |  * not need to be static and can be set up with "static: false". | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | async function runStaticQueryMigration( | 
					
						
							| 
									
										
										
										
											2019-05-08 11:02:37 +02:00
										 |  |  |     tree: Tree, project: AnalyzedProject, selectedStrategy: SELECTED_STRATEGY, | 
					
						
							| 
									
										
										
										
											2019-05-14 20:59:21 +02:00
										 |  |  |     logger: logging.LoggerApi): Promise<string[]> { | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |   const {sourceFiles, typeChecker, host, queryVisitor, tsconfigPath, basePath} = project; | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  |   const printer = ts.createPrinter(); | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  |   const failureMessages: string[] = []; | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |   const templateVisitor = new NgComponentTemplateVisitor(typeChecker); | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // If the "usage" strategy is selected, we also need to add the query visitor
 | 
					
						
							|  |  |  |   // to the analysis visitors so that query usage in templates can be also checked.
 | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  |   if (selectedStrategy === SELECTED_STRATEGY.USAGE) { | 
					
						
							| 
									
										
										
										
											2019-05-03 15:02:08 +02:00
										 |  |  |     sourceFiles.forEach(s => templateVisitor.visitNode(s)); | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const {resolvedQueries, classMetadata} = queryVisitor; | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |   const {resolvedTemplates} = templateVisitor; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  |   if (selectedStrategy === SELECTED_STRATEGY.USAGE) { | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |     // Add all resolved templates to the class metadata if the usage strategy is used. This
 | 
					
						
							|  |  |  |     // is necessary in order to be able to check component templates for static query usage.
 | 
					
						
							|  |  |  |     resolvedTemplates.forEach(template => { | 
					
						
							|  |  |  |       if (classMetadata.has(template.container)) { | 
					
						
							| 
									
										
										
										
											2020-04-02 11:01:43 +02:00
										 |  |  |         classMetadata.get(template.container)!.template = template; | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-22 21:11:29 +02:00
										 |  |  |   let strategy: TimingStrategy; | 
					
						
							|  |  |  |   if (selectedStrategy === SELECTED_STRATEGY.USAGE) { | 
					
						
							|  |  |  |     strategy = new QueryUsageStrategy(classMetadata, typeChecker); | 
					
						
							|  |  |  |   } else if (selectedStrategy === SELECTED_STRATEGY.TESTS) { | 
					
						
							|  |  |  |     strategy = new QueryTestStrategy(); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     strategy = new QueryTemplateStrategy(tsconfigPath, classMetadata, host); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-08 16:16:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 11:02:37 +02:00
										 |  |  |   try { | 
					
						
							|  |  |  |     strategy.setup(); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     if (selectedStrategy === SELECTED_STRATEGY.TEMPLATE) { | 
					
						
							|  |  |  |       logger.warn( | 
					
						
							| 
									
										
										
										
											2019-05-23 02:56:27 +02:00
										 |  |  |           `\nThe template migration strategy uses the Angular compiler ` + | 
					
						
							| 
									
										
										
										
											2019-05-08 11:02:37 +02:00
										 |  |  |           `internally and therefore projects that no longer build successfully after ` + | 
					
						
							|  |  |  |           `the update cannot use the template migration strategy. Please ensure ` + | 
					
						
							| 
									
										
										
										
											2019-05-14 20:59:21 +02:00
										 |  |  |           `there are no AOT compilation errors.\n`); | 
					
						
							| 
									
										
										
										
											2019-05-08 11:02:37 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-14 20:59:21 +02:00
										 |  |  |     // In case the strategy could not be set up properly, we just exit the
 | 
					
						
							|  |  |  |     // migration. We don't want to throw an exception as this could mean
 | 
					
						
							|  |  |  |     // that other migrations are interrupted.
 | 
					
						
							|  |  |  |     logger.warn( | 
					
						
							|  |  |  |         `Could not setup migration strategy for "${project.tsconfigPath}". The ` + | 
					
						
							|  |  |  |         `following error has been reported:\n`); | 
					
						
							|  |  |  |     logger.error(`${e.toString()}\n`); | 
					
						
							| 
									
										
										
										
											2019-05-08 11:02:37 +02:00
										 |  |  |     logger.info( | 
					
						
							| 
									
										
										
										
											2019-05-14 20:59:21 +02:00
										 |  |  |         'Migration can be rerun with: "ng update @angular/core --from 7 --to 8 --migrate-only"\n'); | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |     return []; | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-08 19:37:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  |   // Walk through all source files that contain resolved queries and update
 | 
					
						
							|  |  |  |   // the source files if needed. Note that we need to update multiple queries
 | 
					
						
							|  |  |  |   // within a source file within the same recorder in order to not throw off
 | 
					
						
							|  |  |  |   // the TypeScript node offsets.
 | 
					
						
							|  |  |  |   resolvedQueries.forEach((queries, sourceFile) => { | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |     const relativePath = relative(basePath, sourceFile.fileName); | 
					
						
							|  |  |  |     const update = tree.beginUpdate(relativePath); | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |     // Compute the query timing for all resolved queries and update the
 | 
					
						
							|  |  |  |     // query definitions to explicitly set the determined query timing.
 | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  |     queries.forEach(q => { | 
					
						
							|  |  |  |       const queryExpr = q.decorator.node.expression; | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  |       const {timing, message} = strategy.detectTiming(q); | 
					
						
							| 
									
										
										
										
											2019-04-28 16:40:59 +02:00
										 |  |  |       const result = getTransformedQueryCallExpr(q, timing, !!message); | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 16:40:59 +02:00
										 |  |  |       if (!result) { | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 16:40:59 +02:00
										 |  |  |       const newText = printer.printNode(ts.EmitHint.Unspecified, result.node, sourceFile); | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Replace the existing query decorator call expression with the updated
 | 
					
						
							|  |  |  |       // call expression node.
 | 
					
						
							|  |  |  |       update.remove(queryExpr.getStart(), queryExpr.getWidth()); | 
					
						
							|  |  |  |       update.insertRight(queryExpr.getStart(), newText); | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 16:40:59 +02:00
										 |  |  |       if (result.failureMessage || message) { | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |         const {line, character} = | 
					
						
							|  |  |  |             ts.getLineAndCharacterOfPosition(sourceFile, q.decorator.node.getStart()); | 
					
						
							| 
									
										
										
										
											2019-04-28 16:40:59 +02:00
										 |  |  |         failureMessages.push( | 
					
						
							|  |  |  |             `${relativePath}@${line + 1}:${character + 1}: ${result.failureMessage || message}`); | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tree.commitUpdate(update); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-04-12 17:55:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 00:14:33 +02:00
										 |  |  |   return failureMessages; | 
					
						
							| 
									
										
										
										
											2019-03-13 16:29:25 +01:00
										 |  |  | } |