| 
									
										
										
										
											2016-11-22 09:10:23 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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-09-06 14:57:05 -07:00
										 |  |  | import * as tss from 'typescript/lib/tsserverlibrary'; | 
					
						
							| 
									
										
										
										
											2016-11-22 09:10:23 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import {createLanguageService} from './language_service'; | 
					
						
							|  |  |  | import {TypeScriptServiceHost} from './typescript_host'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 14:57:05 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * A note about importing TypeScript module. | 
					
						
							|  |  |  |  * The TypeScript module is supplied by tsserver at runtime to ensure version | 
					
						
							|  |  |  |  * compatibility. In Angular language service, the rollup output is augmented | 
					
						
							|  |  |  |  * with a "banner" shim that overwrites 'typescript' and | 
					
						
							|  |  |  |  * 'typescript/lib/tsserverlibrary' imports with the value supplied by tsserver. | 
					
						
							|  |  |  |  * This means import of either modules will not be "required", but they'll work | 
					
						
							|  |  |  |  * just like regular imports. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-25 10:50:19 -07:00
										 |  |  | const projectHostMap = new WeakMap<tss.server.Project, TypeScriptServiceHost>(); | 
					
						
							| 
									
										
										
										
											2017-04-25 12:13:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 14:57:05 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Return the external templates discovered through processing all NgModules in | 
					
						
							|  |  |  |  * the specified `project`. | 
					
						
							|  |  |  |  * This function is called in a few situations: | 
					
						
							|  |  |  |  * 1. When a ConfiguredProject is created | 
					
						
							|  |  |  |  *    https://github.com/microsoft/TypeScript/blob/c26c44d5fceb04ea14da20b6ed23449df777ff34/src/server/editorServices.ts#L1755
 | 
					
						
							|  |  |  |  * 2. When updateGraph() is called on a Project | 
					
						
							|  |  |  |  *    https://github.com/microsoft/TypeScript/blob/c26c44d5fceb04ea14da20b6ed23449df777ff34/src/server/project.ts#L915
 | 
					
						
							|  |  |  |  * @param project Most likely a ConfiguredProject | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function getExternalFiles(project: tss.server.Project): string[] { | 
					
						
							|  |  |  |   if (!project.hasRoots()) { | 
					
						
							|  |  |  |     // During project initialization where there is no root files yet we should
 | 
					
						
							|  |  |  |     // not do any work.
 | 
					
						
							|  |  |  |     return []; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   const ngLSHost = projectHostMap.get(project); | 
					
						
							|  |  |  |   if (!ngLSHost) { | 
					
						
							|  |  |  |     // Without an Angular host there is no way to get template references.
 | 
					
						
							|  |  |  |     return []; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   const templates = ngLSHost.getTemplateReferences(); | 
					
						
							|  |  |  |   const logger = project.projectService.logger; | 
					
						
							|  |  |  |   if (logger.hasLevel(tss.server.LogLevel.verbose)) { | 
					
						
							|  |  |  |     // Log external files to help debugging.
 | 
					
						
							|  |  |  |     logger.info(`External files in ${project.projectName}: ${JSON.stringify(templates)}`); | 
					
						
							| 
									
										
										
										
											2017-04-25 12:13:06 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-09-06 14:57:05 -07:00
										 |  |  |   return templates; | 
					
						
							| 
									
										
										
										
											2017-04-25 12:13:06 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-31 10:55:45 -07:00
										 |  |  | export function create(info: tss.server.PluginCreateInfo): tss.LanguageService { | 
					
						
							|  |  |  |   const {project, languageService: tsLS, languageServiceHost: tsLSHost, config} = info; | 
					
						
							|  |  |  |   // This plugin could operate under two different modes:
 | 
					
						
							|  |  |  |   // 1. TS + Angular
 | 
					
						
							|  |  |  |   //    Plugin augments TS language service to provide additional Angular
 | 
					
						
							|  |  |  |   //    information. This only works with inline templates and is meant to be
 | 
					
						
							|  |  |  |   //    used as a local plugin (configured via tsconfig.json)
 | 
					
						
							|  |  |  |   // 2. Angular only
 | 
					
						
							|  |  |  |   //    Plugin only provides information on Angular templates, no TS info at all.
 | 
					
						
							|  |  |  |   //    This effectively disables native TS features and is meant for internal
 | 
					
						
							|  |  |  |   //    use only.
 | 
					
						
							|  |  |  |   const angularOnly = config ? config.angularOnly === true : false; | 
					
						
							|  |  |  |   const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS); | 
					
						
							|  |  |  |   const ngLS = createLanguageService(ngLSHost); | 
					
						
							|  |  |  |   projectHostMap.set(project, ngLSHost); | 
					
						
							| 
									
										
										
										
											2017-01-06 20:43:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 13:07:32 -07:00
										 |  |  |   function getCompletionsAtPosition( | 
					
						
							|  |  |  |       fileName: string, position: number, | 
					
						
							|  |  |  |       options: tss.GetCompletionsAtPositionOptions | undefined) { | 
					
						
							| 
									
										
										
										
											2019-07-31 10:55:45 -07:00
										 |  |  |     if (!angularOnly) { | 
					
						
							|  |  |  |       const results = tsLS.getCompletionsAtPosition(fileName, position, options); | 
					
						
							|  |  |  |       if (results && results.entries.length) { | 
					
						
							|  |  |  |         // If TS could answer the query, then return results immediately.
 | 
					
						
							|  |  |  |         return results; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-08-12 16:54:36 -07:00
										 |  |  |     return ngLS.getCompletionsAt(fileName, position); | 
					
						
							| 
									
										
										
										
											2019-08-01 13:07:32 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-01-06 20:43:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 13:07:32 -07:00
										 |  |  |   function getQuickInfoAtPosition(fileName: string, position: number): tss.QuickInfo|undefined { | 
					
						
							|  |  |  |     if (!angularOnly) { | 
					
						
							|  |  |  |       const result = tsLS.getQuickInfoAtPosition(fileName, position); | 
					
						
							|  |  |  |       if (result) { | 
					
						
							|  |  |  |         // If TS could answer the query, then return results immediately.
 | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ngLS.getHoverAt(fileName, position); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-01-06 20:43:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 13:07:32 -07:00
										 |  |  |   function getSemanticDiagnostics(fileName: string): tss.Diagnostic[] { | 
					
						
							| 
									
										
										
										
											2019-07-31 10:55:45 -07:00
										 |  |  |     const results: tss.Diagnostic[] = []; | 
					
						
							|  |  |  |     if (!angularOnly) { | 
					
						
							| 
									
										
										
										
											2019-08-09 15:52:49 -07:00
										 |  |  |       results.push(...tsLS.getSemanticDiagnostics(fileName)); | 
					
						
							| 
									
										
										
										
											2019-07-31 10:55:45 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     // For semantic diagnostics we need to combine both TS + Angular results
 | 
					
						
							| 
									
										
										
										
											2019-08-09 15:52:49 -07:00
										 |  |  |     results.push(...ngLS.getDiagnostics(fileName)); | 
					
						
							| 
									
										
										
										
											2019-07-31 10:55:45 -07:00
										 |  |  |     return results; | 
					
						
							| 
									
										
										
										
											2019-08-01 13:07:32 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-11-22 09:10:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 13:07:32 -07:00
										 |  |  |   function getDefinitionAtPosition( | 
					
						
							|  |  |  |       fileName: string, position: number): ReadonlyArray<tss.DefinitionInfo>|undefined { | 
					
						
							|  |  |  |     if (!angularOnly) { | 
					
						
							|  |  |  |       const results = tsLS.getDefinitionAtPosition(fileName, position); | 
					
						
							|  |  |  |       if (results) { | 
					
						
							|  |  |  |         // If TS could answer the query, then return results immediately.
 | 
					
						
							|  |  |  |         return results; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const result = ngLS.getDefinitionAt(fileName, position); | 
					
						
							|  |  |  |     if (!result || !result.definitions || !result.definitions.length) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return result.definitions; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-01-06 20:43:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 13:07:32 -07:00
										 |  |  |   function getDefinitionAndBoundSpan( | 
					
						
							|  |  |  |       fileName: string, position: number): tss.DefinitionInfoAndBoundSpan|undefined { | 
					
						
							|  |  |  |     if (!angularOnly) { | 
					
						
							|  |  |  |       const result = tsLS.getDefinitionAndBoundSpan(fileName, position); | 
					
						
							|  |  |  |       if (result) { | 
					
						
							|  |  |  |         // If TS could answer the query, then return results immediately.
 | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ngLS.getDefinitionAt(fileName, position); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-01-06 20:43:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 13:07:32 -07:00
										 |  |  |   const proxy: tss.LanguageService = Object.assign( | 
					
						
							|  |  |  |       // First clone the original TS language service
 | 
					
						
							|  |  |  |       {}, tsLS, | 
					
						
							|  |  |  |       // Then override the methods supported by Angular language service
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |           getCompletionsAtPosition, getQuickInfoAtPosition, getSemanticDiagnostics, | 
					
						
							|  |  |  |           getDefinitionAtPosition, getDefinitionAndBoundSpan, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-01-06 20:43:17 -08:00
										 |  |  |   return proxy; | 
					
						
							|  |  |  | } |