| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Copyright 2019 Google Inc. All rights reserved. | 
					
						
							|  |  |  |  * Modifications copyright (c) Microsoft Corporation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ts = require('typescript'); | 
					
						
							|  |  |  | const path = require('path'); | 
					
						
							|  |  |  | const Documentation = require('./Documentation'); | 
					
						
							|  |  |  | const EventEmitter = require('events'); | 
					
						
							| 
									
										
										
										
											2020-08-13 16:00:23 -07:00
										 |  |  | module.exports = { checkSources }; | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {!Array<!import('../Source')>} sources | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-04-01 14:42:47 -07:00
										 |  |  | function checkSources(sources) { | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   // special treatment for Events.js
 | 
					
						
							|  |  |  |   const classEvents = new Map(); | 
					
						
							| 
									
										
										
										
											2020-02-24 18:24:02 -08:00
										 |  |  |   const eventsSources = sources.filter(source => source.name().startsWith('events.')); | 
					
						
							| 
									
										
										
										
											2019-12-20 13:07:14 -08:00
										 |  |  |   for (const eventsSource of eventsSources) { | 
					
						
							| 
									
										
										
										
											2020-01-24 09:04:54 -08:00
										 |  |  |     const {Events} = require(eventsSource.filePath().endsWith('.js') ? eventsSource.filePath() : eventsSource.filePath().replace(/\bsrc\b/, 'lib').replace('.ts', '.js')); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     for (const [className, events] of Object.entries(Events)) | 
					
						
							|  |  |  |       classEvents.set(className, Array.from(Object.values(events)).filter(e => typeof e === 'string').map(e => Documentation.Member.createEvent(e))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const excludeClasses = new Set([]); | 
					
						
							|  |  |  |   const program = ts.createProgram({ | 
					
						
							|  |  |  |     options: { | 
					
						
							|  |  |  |       allowJs: true, | 
					
						
							|  |  |  |       target: ts.ScriptTarget.ESNext, | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  |       strict: true | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     }, | 
					
						
							|  |  |  |     rootNames: sources.map(source => source.filePath()) | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   const checker = program.getTypeChecker(); | 
					
						
							|  |  |  |   const sourceFiles = program.getSourceFiles(); | 
					
						
							| 
									
										
										
										
											2020-01-07 11:55:24 -08:00
										 |  |  |   const errors = []; | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |   const apiClassNames = new Set(); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   /** @type {!Array<!Documentation.Class>} */ | 
					
						
							|  |  |  |   const classes = []; | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |   /** @type {!Map<string, string[]>} */ | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   const inheritance = new Map(); | 
					
						
							|  |  |  |   sourceFiles.filter(x => !x.fileName.includes('node_modules')).map(x => visit(x)); | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |   const documentation = new Documentation(recreateClassesWithInheritance(classes, inheritance).filter(cls => apiClassNames.has(cls.name))); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return {errors, documentation}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {!Array<!Documentation.Class>} classes | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |    * @param {!Map<string, string[]>} inheritance | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |    * @return {!Array<!Documentation.Class>} | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function recreateClassesWithInheritance(classes, inheritance) { | 
					
						
							|  |  |  |     const classesByName = new Map(classes.map(cls => [cls.name, cls])); | 
					
						
							|  |  |  |     return classes.map(cls => { | 
					
						
							|  |  |  |       const membersMap = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |       const visit = cls => { | 
					
						
							|  |  |  |         if (!cls) | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         for (const member of cls.membersArray) { | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |           // Member was overridden.
 | 
					
						
							|  |  |  |           const memberId = member.kind + ':' + member.name; | 
					
						
							|  |  |  |           if (membersMap.has(memberId)) | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |           membersMap.set(memberId, member); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |         const parents = inheritance.get(cls.name) || []; | 
					
						
							|  |  |  |         for (const parent of parents) | 
					
						
							|  |  |  |           visit(classesByName.get(parent)); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       visit(cls); | 
					
						
							| 
									
										
										
										
											2020-07-24 20:40:21 -07:00
										 |  |  |       return new Documentation.Class(cls.name, Array.from(membersMap.values()), undefined, cls.comment, cls.templates); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {!ts.Node} node | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function visit(node) { | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |     const fileName = node.getSourceFile().fileName; | 
					
						
							|  |  |  |     if (ts.isClassDeclaration(node) || ts.isClassExpression(node) || ts.isInterfaceDeclaration(node)) { | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       const symbol = node.name ? checker.getSymbolAtLocation(node.name) : node.symbol; | 
					
						
							|  |  |  |       let className = symbol.getName(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (className === '__class') { | 
					
						
							|  |  |  |         let parent = node; | 
					
						
							|  |  |  |         while (parent.parent) | 
					
						
							|  |  |  |           parent = parent.parent; | 
					
						
							|  |  |  |         className = path.basename(parent.fileName,  '.js'); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-07-31 17:00:36 -07:00
										 |  |  |       if (className && !excludeClasses.has(className) && !fileName.endsWith('/protocol.ts')) { | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |         excludeClasses.add(className); | 
					
						
							| 
									
										
										
										
											2020-08-13 16:00:23 -07:00
										 |  |  |         classes.push(serializeClass(className, symbol, node)); | 
					
						
							|  |  |  |         inheritance.set(className, parentClasses(node)); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |     if (fileName.endsWith('/api.ts') && ts.isExportSpecifier(node)) | 
					
						
							| 
									
										
										
										
											2020-08-13 16:00:23 -07:00
										 |  |  |       apiClassNames.add((node.propertyName || node.name).text); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     ts.forEachChild(node, visit); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |   function parentClasses(classNode) { | 
					
						
							|  |  |  |     const parents = []; | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     for (const herigateClause of classNode.heritageClauses || []) { | 
					
						
							|  |  |  |       for (const heritageType of herigateClause.types) { | 
					
						
							| 
									
										
										
										
											2019-11-28 12:50:52 -08:00
										 |  |  |         let expression = heritageType.expression; | 
					
						
							|  |  |  |         if (expression.kind === ts.SyntaxKind.PropertyAccessExpression) | 
					
						
							|  |  |  |           expression = expression.name; | 
					
						
							|  |  |  |         if (classNode.name.escapedText !== expression.escapedText) | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |           parents.push(expression.escapedText); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |     return parents; | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * @param {ts.Symbol} symbol | 
					
						
							|  |  |  |    * @param {string[]=} circular | 
					
						
							|  |  |  |    * @param {boolean=} parentRequired | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function serializeSymbol(symbol, circular = [], parentRequired = true) { | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     const type = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration); | 
					
						
							|  |  |  |     const name = symbol.getName(); | 
					
						
							| 
									
										
										
										
											2019-12-10 11:15:14 -08:00
										 |  |  |     if (symbol.valueDeclaration && symbol.valueDeclaration.dotDotDotToken) { | 
					
						
							| 
									
										
										
										
											2019-11-26 08:52:47 -08:00
										 |  |  |       const innerType = serializeType(type.typeArguments ? type.typeArguments[0] : type, circular); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       innerType.name = '...' + innerType.name; | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  |       const required = false; | 
					
						
							|  |  |  |       return Documentation.Member.createProperty('...' + name, innerType, undefined, required); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const required = parentRequired && !typeHasUndefined(type); | 
					
						
							|  |  |  |     return Documentation.Member.createProperty(name, serializeType(type, circular), undefined, required); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {!ts.Type} type | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function typeHasUndefined(type) { | 
					
						
							|  |  |  |     if (!type.isUnion()) | 
					
						
							|  |  |  |       return type.flags & ts.TypeFlags.Undefined; | 
					
						
							|  |  |  |     return type.types.some(typeHasUndefined); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {!ts.Type} type | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2020-03-18 16:23:25 -07:00
										 |  |  |   function isNotUndefined(type) { | 
					
						
							|  |  |  |      return !(type.flags & ts.TypeFlags.Undefined); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {!ts.ObjectType} type | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function isRegularObject(type) { | 
					
						
							|  |  |  |     if (type.isIntersection()) | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     if (!type.objectFlags) | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     if (!('aliasSymbol' in type)) | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     if (type.getConstructSignatures().length) | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     if (type.getCallSignatures().length) | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     if (type.isUnion()) | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {!ts.Type} type | 
					
						
							|  |  |  |    * @return {!Documentation.Type} | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function serializeType(type, circular = []) { | 
					
						
							| 
									
										
										
										
											2020-03-20 15:08:17 -07:00
										 |  |  |     let typeName = checker.typeToString(type).replace(/SmartHandle/g, 'Handle'); | 
					
						
							| 
									
										
										
										
											2020-07-24 20:40:21 -07:00
										 |  |  |     if (typeName === 'any') | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       typeName = 'Object'; | 
					
						
							|  |  |  |     const nextCircular = [typeName].concat(circular); | 
					
						
							| 
									
										
										
										
											2020-04-23 14:45:57 -07:00
										 |  |  |     const stringIndexType = type.getStringIndexType(); | 
					
						
							|  |  |  |     if (stringIndexType) { | 
					
						
							|  |  |  |       return new Documentation.Type(`Object<string, ${serializeType(stringIndexType, circular).name}>`); | 
					
						
							|  |  |  |     } else if (isRegularObject(type)) { | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       let properties = undefined; | 
					
						
							|  |  |  |       if (!circular.includes(typeName)) | 
					
						
							| 
									
										
										
										
											2020-07-31 17:00:36 -07:00
										 |  |  |         properties = getTypeProperties(type).map(property => serializeSymbol(property, nextCircular)); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       return new Documentation.Type('Object', properties); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-12-03 10:51:41 -08:00
										 |  |  |     if (type.isUnion() && (typeName.includes('|') || type.types.every(type => type.isStringLiteral() || type.intrinsicName === 'number'))) { | 
					
						
							| 
									
										
										
										
											2020-08-17 10:47:21 -07:00
										 |  |  |       const types = type.types.filter(isNotUndefined).map((type, index) => { | 
					
						
							|  |  |  |         return { isLiteral: type.isStringLiteral(), serialized: serializeType(type, circular), index }; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       types.sort((a, b) => { | 
					
						
							|  |  |  |         if (!a.isLiteral || !b.isLiteral) | 
					
						
							|  |  |  |           return a.index - b.index; | 
					
						
							|  |  |  |         return a.serialized.name.localeCompare(b.serialized.name); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       const name = types.map(type => type.serialized.name).join('|'); | 
					
						
							|  |  |  |       const properties = [].concat(...types.map(type => type.serialized.properties)); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       return new Documentation.Type(name.replace(/false\|true/g, 'boolean'), properties); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-12-05 09:54:50 -08:00
										 |  |  |     if (type.typeArguments && type.symbol) { | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       const properties = []; | 
					
						
							|  |  |  |       const innerTypeNames = []; | 
					
						
							|  |  |  |       for (const typeArgument of type.typeArguments) { | 
					
						
							|  |  |  |         const innerType = serializeType(typeArgument, nextCircular); | 
					
						
							|  |  |  |         if (innerType.properties) | 
					
						
							|  |  |  |           properties.push(...innerType.properties); | 
					
						
							|  |  |  |         innerTypeNames.push(innerType.name); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-02-06 16:26:36 -08:00
										 |  |  |       if (innerTypeNames.length === 0 || (innerTypeNames.length === 1 && innerTypeNames[0] === 'void')) | 
					
						
							| 
									
										
										
										
											2020-08-13 16:00:23 -07:00
										 |  |  |         return new Documentation.Type(type.symbol.name); | 
					
						
							|  |  |  |       return new Documentation.Type(`${type.symbol.name}<${innerTypeNames.join(', ')}>`, properties); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-08-13 16:00:23 -07:00
										 |  |  |     return new Documentation.Type(typeName, []); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {string} className | 
					
						
							|  |  |  |    * @param {!ts.Symbol} symbol | 
					
						
							|  |  |  |    * @return {} | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function serializeClass(className, symbol, node) { | 
					
						
							|  |  |  |     /** @type {!Array<!Documentation.Member>} */ | 
					
						
							|  |  |  |     const members = classEvents.get(className) || []; | 
					
						
							| 
									
										
										
										
											2020-03-20 01:30:35 -07:00
										 |  |  |     const templates = []; | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     for (const [name, member] of symbol.members || []) { | 
					
						
							| 
									
										
										
										
											2020-01-08 14:04:33 -08:00
										 |  |  |       if (className === 'Error') | 
					
						
							|  |  |  |         continue; | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       if (name.startsWith('_')) | 
					
						
							|  |  |  |         continue; | 
					
						
							| 
									
										
										
										
											2020-08-16 14:41:33 -07:00
										 |  |  |       if (member.valueDeclaration && ts.getCombinedModifierFlags(member.valueDeclaration) & ts.ModifierFlags.Private) | 
					
						
							|  |  |  |         continue; | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |       if (EventEmitter.prototype.hasOwnProperty(name)) | 
					
						
							|  |  |  |         continue; | 
					
						
							|  |  |  |       const memberType = checker.getTypeOfSymbolAtLocation(member, member.valueDeclaration); | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  |       const signature = signatureForType(memberType); | 
					
						
							| 
									
										
										
										
											2020-03-20 01:30:35 -07:00
										 |  |  |       if (member.flags & ts.SymbolFlags.TypeParameter) | 
					
						
							|  |  |  |         templates.push(name); | 
					
						
							|  |  |  |       else if (signature) | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |         members.push(serializeSignature(name, signature)); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         members.push(serializeProperty(name, memberType)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 01:30:35 -07:00
										 |  |  |     return new Documentation.Class(className, members, undefined, undefined, templates); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * @param {ts.Type} type | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function signatureForType(type) { | 
					
						
							|  |  |  |     const signatures = type.getCallSignatures(); | 
					
						
							|  |  |  |     if (signatures.length) | 
					
						
							| 
									
										
										
										
											2020-03-23 17:22:10 -07:00
										 |  |  |       return signatures[signatures.length - 1]; | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  |     if (type.isUnion()) { | 
					
						
							| 
									
										
										
										
											2020-03-18 16:23:25 -07:00
										 |  |  |       const innerTypes = type.types.filter(isNotUndefined); | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  |       if (innerTypes.length === 1) | 
					
						
							|  |  |  |         return signatureForType(innerTypes[0]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * @param {string} name | 
					
						
							|  |  |  |    * @param {!ts.Signature} signature | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function serializeSignature(name, signature) { | 
					
						
							| 
									
										
										
										
											2020-03-03 17:29:12 -08:00
										 |  |  |     const minArgumentCount = signature.minArgumentCount || 0; | 
					
						
							|  |  |  |     const parameters = signature.parameters.map((s, index) => serializeSymbol(s, [], index < minArgumentCount)); | 
					
						
							| 
									
										
										
										
											2020-03-20 01:30:35 -07:00
										 |  |  |     const templates = signature.typeParameters ? signature.typeParameters.map(t => t.symbol.name) : []; | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     const returnType = serializeType(signature.getReturnType()); | 
					
						
							| 
									
										
										
										
											2020-03-20 01:30:35 -07:00
										 |  |  |     return Documentation.Member.createMethod(name, parameters, returnType.name !== 'void' ? returnType : null, undefined, undefined, templates); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {string} name | 
					
						
							|  |  |  |    * @param {!ts.Type} type | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function serializeProperty(name, type) { | 
					
						
							|  |  |  |     return Documentation.Member.createProperty(name, serializeType(type)); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-07-31 17:00:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {!ts.Type} type | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function getTypeProperties(type) { | 
					
						
							|  |  |  |     if (type.aliasSymbol && type.aliasSymbol.escapedName === 'Pick') { | 
					
						
							|  |  |  |       const props = getTypeProperties(type.aliasTypeArguments[0]); | 
					
						
							|  |  |  |       const pickNames = type.aliasTypeArguments[1].types.map(t => t.value); | 
					
						
							|  |  |  |       return props.filter(p => pickNames.includes(p.getName())); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!type.isIntersection()) | 
					
						
							|  |  |  |       return type.getProperties(); | 
					
						
							|  |  |  |     let props = []; | 
					
						
							|  |  |  |     for (const innerType of type.types) { | 
					
						
							|  |  |  |       let innerProps = getTypeProperties(innerType); | 
					
						
							|  |  |  |       props = props.filter(p => !innerProps.find(e => e.getName() === p.getName())); | 
					
						
							| 
									
										
										
										
											2020-10-01 11:06:19 -07:00
										 |  |  |       props = props.filter(p => p.getName() !== '_tracePath' && p.getName() !== '_traceResourcesPath'); | 
					
						
							| 
									
										
										
										
											2020-07-31 17:00:36 -07:00
										 |  |  |       props.push(...innerProps); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return props; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  | } |