| 
									
										
										
										
											2016-10-23 22:37:15 +02: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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-08 10:27:54 -08:00
										 |  |  | import * as ts from 'typescript'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  | import {MetadataValue} from './schema'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-08 10:27:54 -08:00
										 |  |  | export class Symbols { | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |   private _symbols: Map<string, MetadataValue>; | 
					
						
							| 
									
										
										
										
											2016-03-08 10:27:54 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |   constructor(private sourceFile: ts.SourceFile) {} | 
					
						
							| 
									
										
										
										
											2016-03-08 10:27:54 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |   resolve(name: string): MetadataValue|undefined { return this.symbols.get(name); } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   define(name: string, value: MetadataValue) { this.symbols.set(name, value); } | 
					
						
							| 
									
										
										
										
											2016-03-08 10:27:54 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |   has(name: string): boolean { return this.symbols.has(name); } | 
					
						
							| 
									
										
										
										
											2016-03-08 10:27:54 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |   private get symbols(): Map<string, MetadataValue> { | 
					
						
							|  |  |  |     let result = this._symbols; | 
					
						
							|  |  |  |     if (!result) { | 
					
						
							| 
									
										
										
										
											2016-07-25 15:27:09 -07:00
										 |  |  |       result = this._symbols = new Map<string, MetadataValue>(); | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |       populateBuiltins(result); | 
					
						
							|  |  |  |       this.buildImports(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private buildImports(): void { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |     const symbols = this._symbols; | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |     // Collect the imported symbols into this.symbols
 | 
					
						
							|  |  |  |     const stripQuotes = (s: string) => s.replace(/^['"]|['"]$/g, ''); | 
					
						
							|  |  |  |     const visit = (node: ts.Node) => { | 
					
						
							|  |  |  |       switch (node.kind) { | 
					
						
							|  |  |  |         case ts.SyntaxKind.ImportEqualsDeclaration: | 
					
						
							|  |  |  |           const importEqualsDeclaration = <ts.ImportEqualsDeclaration>node; | 
					
						
							|  |  |  |           if (importEqualsDeclaration.moduleReference.kind === | 
					
						
							|  |  |  |               ts.SyntaxKind.ExternalModuleReference) { | 
					
						
							|  |  |  |             const externalReference = | 
					
						
							|  |  |  |                 <ts.ExternalModuleReference>importEqualsDeclaration.moduleReference; | 
					
						
							|  |  |  |             // An `import <identifier> = require(<module-specifier>);
 | 
					
						
							| 
									
										
										
										
											2016-06-17 13:11:00 -07:00
										 |  |  |             if (!externalReference.expression.parent) { | 
					
						
							|  |  |  |               // The `parent` field of a node is set by the TypeScript binder (run as
 | 
					
						
							|  |  |  |               // part of the type checker). Setting it here allows us to call `getText()`
 | 
					
						
							|  |  |  |               // even if the `SourceFile` was not type checked (which looks for `SourceFile`
 | 
					
						
							|  |  |  |               // in the parent chain). This doesn't damage the node as the binder unconditionally
 | 
					
						
							|  |  |  |               // sets the parent.
 | 
					
						
							|  |  |  |               externalReference.expression.parent = externalReference; | 
					
						
							|  |  |  |               externalReference.parent = this.sourceFile; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |             const from = stripQuotes(externalReference.expression.getText()); | 
					
						
							|  |  |  |             symbols.set(importEqualsDeclaration.name.text, {__symbolic: 'reference', module: from}); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             symbols.set( | 
					
						
							|  |  |  |                 importEqualsDeclaration.name.text, | 
					
						
							|  |  |  |                 {__symbolic: 'error', message: `Unsupported import syntax`}); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         case ts.SyntaxKind.ImportDeclaration: | 
					
						
							|  |  |  |           const importDecl = <ts.ImportDeclaration>node; | 
					
						
							|  |  |  |           if (!importDecl.importClause) { | 
					
						
							|  |  |  |             // An `import <module-specifier>` clause which does not bring symbols into scope.
 | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2016-06-17 13:11:00 -07:00
										 |  |  |           if (!importDecl.moduleSpecifier.parent) { | 
					
						
							|  |  |  |             // See note above in the `ImportEqualDeclaration` case.
 | 
					
						
							|  |  |  |             importDecl.moduleSpecifier.parent = importDecl; | 
					
						
							|  |  |  |             importDecl.parent = this.sourceFile; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |           const from = stripQuotes(importDecl.moduleSpecifier.getText()); | 
					
						
							|  |  |  |           if (importDecl.importClause.name) { | 
					
						
							|  |  |  |             // An `import <identifier> form <module-specifier>` clause. Record the defualt symbol.
 | 
					
						
							|  |  |  |             symbols.set( | 
					
						
							|  |  |  |                 importDecl.importClause.name.text, | 
					
						
							|  |  |  |                 {__symbolic: 'reference', module: from, default: true}); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           const bindings = importDecl.importClause.namedBindings; | 
					
						
							|  |  |  |           if (bindings) { | 
					
						
							|  |  |  |             switch (bindings.kind) { | 
					
						
							|  |  |  |               case ts.SyntaxKind.NamedImports: | 
					
						
							|  |  |  |                 // An `import { [<identifier> [, <identifier>] } from <module-specifier>` clause
 | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |                 for (const binding of (<ts.NamedImports>bindings).elements) { | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  |                   symbols.set(binding.name.text, { | 
					
						
							|  |  |  |                     __symbolic: 'reference', | 
					
						
							|  |  |  |                     module: from, | 
					
						
							|  |  |  |                     name: binding.propertyName ? binding.propertyName.text : binding.name.text | 
					
						
							|  |  |  |                   }); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |               case ts.SyntaxKind.NamespaceImport: | 
					
						
							|  |  |  |                 // An `input * as <identifier> from <module-specifier>` clause.
 | 
					
						
							|  |  |  |                 symbols.set( | 
					
						
							|  |  |  |                     (<ts.NamespaceImport>bindings).name.text, | 
					
						
							|  |  |  |                     {__symbolic: 'reference', module: from}); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       ts.forEachChild(node, visit); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     if (this.sourceFile) { | 
					
						
							|  |  |  |       ts.forEachChild(this.sourceFile, visit); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-03-08 10:27:54 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-05-31 11:00:39 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | function populateBuiltins(symbols: Map<string, MetadataValue>) { | 
					
						
							|  |  |  |   // From lib.core.d.ts (all "define const")
 | 
					
						
							|  |  |  |   ['Object', 'Function', 'String', 'Number', 'Array', 'Boolean', 'Map', 'NaN', 'Infinity', 'Math', | 
					
						
							|  |  |  |    'Date', 'RegExp', 'Error', 'Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', | 
					
						
							|  |  |  |    'TypeError', 'URIError', 'JSON', 'ArrayBuffer', 'DataView', 'Int8Array', 'Uint8Array', | 
					
						
							|  |  |  |    'Uint8ClampedArray', 'Uint16Array', 'Int16Array', 'Int32Array', 'Uint32Array', 'Float32Array', | 
					
						
							|  |  |  |    'Float64Array'] | 
					
						
							|  |  |  |       .forEach(name => symbols.set(name, {__symbolic: 'reference', name})); | 
					
						
							| 
									
										
										
										
											2016-07-25 15:27:09 -07:00
										 |  |  | } |