| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // @ts-check
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const path = require('path'); | 
					
						
							|  |  |  | const Documentation = require('./documentation'); | 
					
						
							|  |  |  | const XmlDoc = require('./xmlDocumentation') | 
					
						
							|  |  |  | const PROJECT_DIR = path.join(__dirname, '..', '..'); | 
					
						
							|  |  |  | const fs = require('fs'); | 
					
						
							|  |  |  | const { parseApi } = require('./api_parser'); | 
					
						
							|  |  |  | const { Type } = require('./documentation'); | 
					
						
							|  |  |  | const { EOL } = require('os'); | 
					
						
							| 
									
										
										
										
											2021-05-03 10:23:36 +02:00
										 |  |  | const { execSync } = require('child_process'); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | const maxDocumentationColumnWidth = 80; | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | Error.stackTraceLimit = 100; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** @type {Map<string, Documentation.Type>} */ | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | const modelTypes = new Map(); // this will hold types that we discover, because of .NET specifics, like results
 | 
					
						
							| 
									
										
										
										
											2021-03-03 19:36:27 +01:00
										 |  |  | /** @type {Map<string, string>} */ | 
					
						
							|  |  |  | const documentedResults = new Map(); // will hold documentation for new types
 | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | /** @type {Map<string, string[]>} */ | 
					
						
							|  |  |  | const enumTypes = new Map(); | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  | /** @type {Map<string, Documentation.Type>} */ | 
					
						
							|  |  |  | const optionTypes = new Map(); | 
					
						
							| 
									
										
										
										
											2021-03-29 05:22:06 -03:00
										 |  |  | const customTypeNames = new Map([ | 
					
						
							|  |  |  |   ['domcontentloaded', 'DOMContentLoaded'], | 
					
						
							|  |  |  |   ['networkidle', 'NetworkIdle'], | 
					
						
							|  |  |  | ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-22 11:47:13 -07:00
										 |  |  | const outputDir = process.argv[2] || path.join(__dirname, 'generate_types', 'csharp'); | 
					
						
							|  |  |  | const apiDir = path.join(outputDir, 'API', 'Generated'); | 
					
						
							|  |  |  | const optionsDir = path.join(outputDir, 'API', 'Generated', 'Options'); | 
					
						
							|  |  |  | const enumsDir = path.join(outputDir, 'API', 'Generated', 'Enums'); | 
					
						
							|  |  |  | const typesDir = path.join(outputDir, 'API', 'Generated', 'Types'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 11:13:48 +02:00
										 |  |  | for (const dir of [apiDir, optionsDir, enumsDir, typesDir]) | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   fs.mkdirSync(dir, { recursive: true }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const documentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api')); | 
					
						
							|  |  |  | documentation.filterForLanguage('csharp'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | documentation.setLinkRenderer(item => { | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |   const asyncSuffix = item.member && item.member.async ? 'Async' : ''; | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   if (item.clazz) | 
					
						
							|  |  |  |     return `<see cref="I${toTitleCase(item.clazz.name)}"/>`; | 
					
						
							|  |  |  |   else if (item.member) | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |     return `<see cref="I${toTitleCase(item.member.clazz.name)}.${toMemberName(item.member)}${asyncSuffix}"/>`; | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   else if (item.option) | 
					
						
							|  |  |  |     return `<paramref name="${item.option}"/>`; | 
					
						
							|  |  |  |   else if (item.param) | 
					
						
							|  |  |  |     return `<paramref name="${item.param}"/>`; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     throw new Error('Unknown link format.'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // get the template for a class
 | 
					
						
							| 
									
										
										
										
											2021-05-22 11:47:13 -07:00
										 |  |  | const template = fs.readFileSync(path.join(__dirname, 'templates', 'interface.cs'), 'utf-8'); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // map the name to a C# friendly one (we prepend an I to denote an interface)
 | 
					
						
							|  |  |  | const classNameMap = new Map(documentation.classesArray.map(x => [x.name, `I${toTitleCase(x.name)}`])); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // map some types that we know of
 | 
					
						
							|  |  |  | classNameMap.set('Error', 'Exception'); | 
					
						
							|  |  |  | classNameMap.set('TimeoutError', 'TimeoutException'); | 
					
						
							|  |  |  | classNameMap.set('EvaluationArgument', 'object'); | 
					
						
							|  |  |  | classNameMap.set('boolean', 'bool'); | 
					
						
							|  |  |  | classNameMap.set('Serializable', 'T'); | 
					
						
							|  |  |  | classNameMap.set('any', 'object'); | 
					
						
							|  |  |  | classNameMap.set('Buffer', 'byte[]'); | 
					
						
							|  |  |  | classNameMap.set('path', 'string'); | 
					
						
							|  |  |  | classNameMap.set('URL', 'string'); | 
					
						
							|  |  |  | classNameMap.set('RegExp', 'Regex'); | 
					
						
							|  |  |  | classNameMap.set('Readable', 'Stream'); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param {string} kind | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {Documentation.MarkdownNode[]} spec | 
					
						
							|  |  |  |  * @param {string[]} body | 
					
						
							|  |  |  |  * @param {string} folder | 
					
						
							|  |  |  |  * @param {string} extendsName | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-06-15 11:47:24 +02:00
										 |  |  | function writeFile(kind, name, spec, body, folder, extendsName = null, namespace = "Microsoft.Playwright") { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   const out = []; | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |   // console.log(`Generating ${name}`);
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (spec) | 
					
						
							|  |  |  |     out.push(...XmlDoc.renderXmlDoc(spec, maxDocumentationColumnWidth)); | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     let ownDocumentation = documentedResults.get(name); | 
					
						
							|  |  |  |     if (ownDocumentation) { | 
					
						
							|  |  |  |       out.push('/// <summary>'); | 
					
						
							|  |  |  |       out.push(`/// ${ownDocumentation}`); | 
					
						
							|  |  |  |       out.push('/// </summary>'); | 
					
						
							| 
									
										
										
										
											2021-03-03 19:36:27 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   if (extendsName === 'IEventEmitter') | 
					
						
							|  |  |  |     extendsName = null; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |   if (body[0] === '') | 
					
						
							|  |  |  |     body = body.slice(1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |   out.push(`${kind} ${name}${extendsName ? ` : ${extendsName}` : ''}`); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   out.push('{'); | 
					
						
							|  |  |  |   out.push(...body); | 
					
						
							|  |  |  |   out.push('}'); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-15 11:47:24 +02:00
										 |  |  |   let content = template.replace('[NAMESPACE]', namespace).replace('[CONTENT]', out.join(EOL)); | 
					
						
							| 
									
										
										
										
											2021-05-22 11:47:13 -07:00
										 |  |  |   fs.writeFileSync(path.join(folder, name + '.cs'), content); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {Documentation.Class} clazz  | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | function renderClass(clazz) { | 
					
						
							|  |  |  |   const name = classNameMap.get(clazz.name); | 
					
						
							|  |  |  |   if (name === 'TimeoutException') | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   const body = []; | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |   for (const member of clazz.membersArray) { | 
					
						
							|  |  |  |     if (member.alias.startsWith('RunAnd')) | 
					
						
							|  |  |  |       renderMember(member, clazz, { trimRunAndPrefix: true }, body); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |     renderMember(member, clazz, {}, body); | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   writeFile( | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       'public partial interface', | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |       name, | 
					
						
							|  |  |  |       clazz.spec, | 
					
						
							|  |  |  |       body, | 
					
						
							| 
									
										
										
										
											2021-05-22 11:47:13 -07:00
										 |  |  |       apiDir, | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |       clazz.extends ? `I${toTitleCase(clazz.extends)}` : null); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {Documentation.Type} type | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function renderModelType(name, type) { | 
					
						
							|  |  |  |   const body = []; | 
					
						
							|  |  |  |   // TODO: consider how this could be merged with the `translateType` check
 | 
					
						
							|  |  |  |   if (type.union | 
					
						
							|  |  |  |     && type.union[0].name === 'null' | 
					
						
							|  |  |  |     && type.union.length == 2) { | 
					
						
							|  |  |  |     type = type.union[1]; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   if (type.name === 'Array') { | 
					
						
							|  |  |  |     throw new Error('Array at this stage is unexpected.'); | 
					
						
							|  |  |  |   } else if (type.properties) { | 
					
						
							|  |  |  |     for (const member of type.properties) { | 
					
						
							|  |  |  |       let fakeType = new Type(name, null); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       renderMember(member, fakeType, {}, body); | 
					
						
							| 
									
										
										
										
											2021-05-16 09:58:40 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     console.log(type); | 
					
						
							|  |  |  |     throw new Error(`Not sure what to do in this case.`); | 
					
						
							| 
									
										
										
										
											2021-05-03 10:23:36 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-22 11:47:13 -07:00
										 |  |  |   writeFile('public partial class', name, null, body, typesDir); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2021-04-06 07:21:31 -03:00
										 |  |  |  * @param {string} name | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |  * @param {string[]} literals | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | function renderEnum(name, literals) { | 
					
						
							|  |  |  |   const body = []; | 
					
						
							|  |  |  |   for (let literal of literals) { | 
					
						
							|  |  |  |     // strip out the quotes
 | 
					
						
							|  |  |  |     literal = literal.replace(/[\"]/g, ``) | 
					
						
							|  |  |  |     let escapedName = literal.replace(/[-]/g, ' ') | 
					
						
							|  |  |  |       .split(' ') | 
					
						
							|  |  |  |       .map(word => customTypeNames.get(word) || word[0].toUpperCase() + word.substring(1)).join(''); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     body.push(`[EnumMember(Value = "${literal}")]`); | 
					
						
							|  |  |  |     body.push(`${escapedName},`); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |   writeFile('public enum', name, null, body, enumsDir); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-01 18:49:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {Documentation.Type} type | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function renderOptionType(name, type) { | 
					
						
							|  |  |  |   const body = []; | 
					
						
							| 
									
										
										
										
											2021-05-20 20:24:05 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   renderConstructors(name, type, body); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |   for (const member of type.properties) | 
					
						
							| 
									
										
										
										
											2021-05-20 20:24:05 -03:00
										 |  |  |     renderMember(member, member.type, {}, body); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |   writeFile('public class', name, null, body, optionsDir); | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  | for (const element of documentation.classesArray) { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   renderClass(element); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-22 10:13:58 -07:00
										 |  |  | for (let [name, type] of optionTypes) | 
					
						
							|  |  |  |   renderOptionType(name, type); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | for (let [name, type] of modelTypes) | 
					
						
							|  |  |  |   renderModelType(name, type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | for (let [name, literals] of enumTypes) | 
					
						
							|  |  |  |   renderEnum(name, literals); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (process.argv[3] !== "--skip-format") { | 
					
						
							|  |  |  |   // run the formatting tool for .net, to ensure the files are prepped
 | 
					
						
							| 
									
										
										
										
											2021-05-22 11:47:13 -07:00
										 |  |  |   execSync(`dotnet format -f "${outputDir}" --include-generated --fix-whitespace`); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function toArgumentName(name) { | 
					
						
							|  |  |  |   return name === 'event' ? `@${name}` : name; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  /** | 
					
						
							|  |  |  |  * @param {Documentation.Member} member | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  | function toMemberName(member, makeAsync = false) { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   const assumedName = toTitleCase(member.alias || member.name); | 
					
						
							|  |  |  |   if (member.kind === 'interface') | 
					
						
							|  |  |  |     return `I${assumedName}`; | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |   if (makeAsync && member.async) | 
					
						
							|  |  |  |     return assumedName + 'Async'; | 
					
						
							|  |  |  |   if (!makeAsync && assumedName.endsWith('Async')) | 
					
						
							| 
									
										
										
										
											2021-05-19 15:49:44 -07:00
										 |  |  |     return assumedName.substring(0, assumedName.length - 'Async'.length); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   return assumedName; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @returns {string} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function toTitleCase(name) { | 
					
						
							|  |  |  |   return name.charAt(0).toUpperCase() + name.substring(1); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 20:24:05 -03:00
										 |  |  | /** | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {Documentation.Type} type | 
					
						
							|  |  |  |  * @param {string[]} out | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function renderConstructors(name, type, out) { | 
					
						
							|  |  |  |   out.push(`public ${name}(){}`); | 
					
						
							|  |  |  |   out.push(''); | 
					
						
							|  |  |  |   out.push(`public ${name}(${name} clone) {`); | 
					
						
							|  |  |  |   out.push(`if(clone == null) return;`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   type.properties.forEach(p => { | 
					
						
							|  |  |  |     let propType = translateType(p.type, type, t => generateNameDefault(p, name, t, type)); | 
					
						
							|  |  |  |     let propName = toMemberName(p); | 
					
						
							|  |  |  |     const overloads = getPropertyOverloads(propType, p, propName, p.type); | 
					
						
							|  |  |  |     for (let { name } of overloads) | 
					
						
							|  |  |  |       out.push(`${name} = clone.${name};`); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   out.push(`}`); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2021-04-06 07:21:31 -03:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @param {Documentation.Member} member | 
					
						
							|  |  |  |  * @param {Documentation.Class|Documentation.Type} parent | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |  * @param {{nojson?: boolean, trimRunAndPrefix?: boolean}} options | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |  * @param {string[]} out | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  | function renderMember(member, parent, options, out) { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   let name = toMemberName(member); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   if (member.kind === 'method') { | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |     renderMethod(member, parent, name, { mode: 'options', trimRunAndPrefix: options.trimRunAndPrefix }, out); | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** @type string */ | 
					
						
							|  |  |  |   let type = translateType(member.type, parent, t => generateNameDefault(member, name, t, parent)); | 
					
						
							|  |  |  |   if (member.kind === 'event') { | 
					
						
							|  |  |  |     if (!member.type) | 
					
						
							|  |  |  |       throw new Error(`No Event Type for ${name} in ${parent.name}`); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |     out.push(''); | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |     if (member.spec) | 
					
						
							|  |  |  |       out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth)); | 
					
						
							|  |  |  |     out.push(`event EventHandler<${type}> ${name};`); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (member.kind === 'property') { | 
					
						
							|  |  |  |     if (parent && member && member.name === 'children') {  // this is a special hack for Accessibility
 | 
					
						
							|  |  |  |       console.warn(`children property found in ${parent.name}, assuming array.`); | 
					
						
							|  |  |  |       type = `IEnumerable<${parent.name}>`; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-20 20:24:05 -03:00
										 |  |  |     const overloads = getPropertyOverloads(type, member, name, parent); | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |     for (let { type, name, jsonName } of overloads) { | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       out.push(''); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       if (member.spec) | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |         out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth)); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |       if (!member.clazz) | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |         out.push(`${member.required ? '[Required]\n' : ''}[JsonPropertyName("${jsonName}")]`) | 
					
						
							| 
									
										
										
										
											2021-08-25 14:24:18 +02:00
										 |  |  |       if (member.deprecated) | 
					
						
							|  |  |  |         out.push(`[System.Obsolete]`); | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |       if (!type.endsWith('?') && !member.required) | 
					
						
							| 
									
										
										
										
											2021-05-20 20:24:05 -03:00
										 |  |  |         type = `${type}?`; | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |       const requiredSuffix = type.endsWith('?') ? '' : ' = default!;'; | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |       if (member.clazz) | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |         out.push(`public ${type} ${name} { get; }`); | 
					
						
							| 
									
										
										
										
											2021-04-30 01:10:59 -03:00
										 |  |  |       else | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |         out.push(`public ${type} ${name} { get; set; }${requiredSuffix}`); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |   throw new Error(`Problem rendering a member: ${type} - ${name} (${member.kind})`); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 20:24:05 -03:00
										 |  |  | /** | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param {string} type | 
					
						
							|  |  |  |  * @param {Documentation.Member} member | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {Documentation.Class|Documentation.Type} parent | 
					
						
							|  |  |  |  * @returns [{ type: string; name: string; jsonName: string; }] | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function getPropertyOverloads(type, member, name, parent) { | 
					
						
							|  |  |  |   const overloads = []; | 
					
						
							|  |  |  |   if (type) { | 
					
						
							|  |  |  |     let jsonName = member.name; | 
					
						
							|  |  |  |     if (member.type.expression === '[string]|[float]') | 
					
						
							|  |  |  |       jsonName = `${member.name}String`; | 
					
						
							|  |  |  |     overloads.push({ type, name, jsonName }); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     for (const overload of member.type.union) { | 
					
						
							|  |  |  |       const t = translateType(overload, parent, t => generateNameDefault(member, name, t, parent)); | 
					
						
							|  |  |  |       const suffix = toOverloadSuffix(t); | 
					
						
							|  |  |  |       overloads.push({ type: t, name: name + suffix, jsonName: member.name + suffix }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return overloads; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2021-04-06 07:21:31 -03:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @param {Documentation.Member} member | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {Documentation.Type} t | 
					
						
							|  |  |  |  * @param {*} parent | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | function generateNameDefault(member, name, t, parent) { | 
					
						
							|  |  |  |   if (!t.properties | 
					
						
							|  |  |  |     && !t.templates | 
					
						
							|  |  |  |     && !t.union | 
					
						
							|  |  |  |     && t.expression === '[Object]') | 
					
						
							|  |  |  |     return 'object'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // we'd get this call for enums, primarily
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   let enumName = generateEnumNameIfApplicable(t); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   if (!enumName && member) { | 
					
						
							|  |  |  |     if (member.kind === 'method' || member.kind === 'property') { | 
					
						
							| 
									
										
										
										
											2021-03-23 10:44:50 +01:00
										 |  |  |       let names = [ | 
					
						
							|  |  |  |         parent.alias || parent.name, | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |         toTitleCase(member.alias || member.name), | 
					
						
							|  |  |  |         toTitleCase(name), | 
					
						
							| 
									
										
										
										
											2021-03-23 10:44:50 +01:00
										 |  |  |       ]; | 
					
						
							|  |  |  |       if (names[2] === names[1]) | 
					
						
							|  |  |  |         names.pop(); // get rid of duplicates, cheaply
 | 
					
						
							|  |  |  |       let attemptedName = names.pop(); | 
					
						
							|  |  |  |       let typesDiffer = function (left, right) { | 
					
						
							|  |  |  |         if (left.expression && right.expression) | 
					
						
							|  |  |  |           return left.expression !== right.expression; | 
					
						
							|  |  |  |         return JSON.stringify(right.properties) !== JSON.stringify(left.properties); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       while (true) { | 
					
						
							|  |  |  |         // crude attempt at removing plurality
 | 
					
						
							|  |  |  |         if (attemptedName.endsWith('s') | 
					
						
							|  |  |  |           && !["properties", "httpcredentials"].includes(attemptedName.toLowerCase())) | 
					
						
							|  |  |  |           attemptedName = attemptedName.substring(0, attemptedName.length - 1); | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // For some of these we don't want to generate generic types.
 | 
					
						
							|  |  |  |         // For some others we simply did not have the code that was deduping the names.
 | 
					
						
							|  |  |  |         if (attemptedName === 'BoundingBox') | 
					
						
							|  |  |  |           attemptedName = `${parent.name}BoundingBoxResult`; | 
					
						
							|  |  |  |         if (attemptedName === 'BrowserContextCookie') | 
					
						
							|  |  |  |           attemptedName = 'BrowserContextCookiesResult'; | 
					
						
							|  |  |  |         if (attemptedName === 'File') | 
					
						
							|  |  |  |           attemptedName = `FilePayload`; | 
					
						
							|  |  |  |         if (attemptedName === 'Size') | 
					
						
							|  |  |  |           attemptedName = 'RequestSizesResult'; | 
					
						
							|  |  |  |         if (attemptedName === 'ViewportSize' && parent.name === 'Page') | 
					
						
							|  |  |  |           attemptedName = 'PageViewportSizeResult'; | 
					
						
							|  |  |  |         if (attemptedName === 'SecurityDetail') | 
					
						
							|  |  |  |           attemptedName = 'ResponseSecurityDetailsResult'; | 
					
						
							|  |  |  |         if (attemptedName === 'ServerAddr') | 
					
						
							|  |  |  |           attemptedName = 'ResponseServerAddrResult'; | 
					
						
							|  |  |  |         if (attemptedName === 'Timing') | 
					
						
							|  |  |  |           attemptedName = 'RequestTimingResult'; | 
					
						
							|  |  |  |         if (attemptedName === 'HeadersArray') | 
					
						
							|  |  |  |           attemptedName = 'Header'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |         let probableType = modelTypes.get(attemptedName); | 
					
						
							| 
									
										
										
										
											2021-03-23 10:44:50 +01:00
										 |  |  |         if ((probableType && typesDiffer(t, probableType)) | 
					
						
							|  |  |  |           || (["Value"].includes(attemptedName))) { | 
					
						
							|  |  |  |           if (!names.length) | 
					
						
							|  |  |  |             throw new Error(`Ran out of possible names: ${attemptedName}`); | 
					
						
							|  |  |  |           attemptedName = `${names.pop()}${attemptedName}`; | 
					
						
							|  |  |  |           continue; | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2021-05-22 10:13:58 -07:00
										 |  |  |           registerModelType(attemptedName, t); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-03-23 10:44:50 +01:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-03-23 10:44:50 +01:00
										 |  |  |       return attemptedName; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (member.kind === 'event') { | 
					
						
							|  |  |  |       return `${name}Payload`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return enumName || t.name; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * @param {Documentation.Type} type  | 
					
						
							|  |  |  |  * @returns  | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function generateEnumNameIfApplicable(type) { | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   if (!type.union) | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const potentialValues = type.union.filter(u => u.name.startsWith('"')); | 
					
						
							|  |  |  |   if ((potentialValues.length !== type.union.length) | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |     && !(type.union[0].name === 'null' && potentialValues.length === type.union.length - 1)) { | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |     return null; // this isn't an enum, so we don't care, we let the caller generate the name
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   } | 
					
						
							|  |  |  |   return type.name; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Rendering a method is so _special_, with so many weird edge cases, that it | 
					
						
							| 
									
										
										
										
											2021-04-06 07:21:31 -03:00
										 |  |  |  * makes sense to put it separate from the other logic. | 
					
						
							|  |  |  |  * @param {Documentation.Member} member | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |  * @param {Documentation.Class | Documentation.Type} parent | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |  * @param {{ | 
					
						
							|  |  |  |  *   mode: 'options'|'named'|'base', | 
					
						
							|  |  |  |  *   nodocs?: boolean, | 
					
						
							|  |  |  |  *   abstract?: boolean, | 
					
						
							|  |  |  |  *   public?: boolean, | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |  *   trimRunAndPrefix?: boolean, | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |  * }} options | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |  * @param {string[]} out | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  | function renderMethod(member, parent, name, options, out) { | 
					
						
							|  |  |  |   out.push(''); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |   if (options.trimRunAndPrefix) | 
					
						
							|  |  |  |     name = name.substring('RunAnd'.length); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   /** @type {Map<string, string[]>} */ | 
					
						
							|  |  |  |   const paramDocs = new Map(); | 
					
						
							|  |  |  |   const addParamsDoc = (paramName, docs) => { | 
					
						
							|  |  |  |     if (paramName.startsWith('@')) | 
					
						
							|  |  |  |       paramName = paramName.substring(1); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |     if (paramDocs.get(paramName) && paramDocs.get(paramName) !== docs) | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       throw new Error(`Parameter ${paramName} already exists in the docs.`); | 
					
						
							|  |  |  |     paramDocs.set(paramName, docs); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  |   let type = translateType(member.type, parent, t => generateNameDefault(member, name, t, parent), false, true); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // TODO: this is something that will probably go into the docs
 | 
					
						
							| 
									
										
										
										
											2021-03-01 18:49:14 +01:00
										 |  |  |   // translate simple getters into read-only properties, and simple
 | 
					
						
							|  |  |  |   // set-only methods to settable properties
 | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   if (member.args.size == 0 | 
					
						
							|  |  |  |     && type !== 'void' | 
					
						
							| 
									
										
										
										
											2021-05-14 11:48:07 -03:00
										 |  |  |     && !name.startsWith('Get') | 
					
						
							| 
									
										
										
										
											2021-05-18 10:55:03 -07:00
										 |  |  |     && !name.startsWith('PostDataJSON') | 
					
						
							| 
									
										
										
										
											2021-05-14 11:48:07 -03:00
										 |  |  |     && !name.startsWith('As')) { | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |     if (!member.async) { | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       if (member.spec && !options.nodocs) | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |         out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth)); | 
					
						
							|  |  |  |       out.push(`${type} ${name} { get; }`); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // HACK: special case for generics handling!
 | 
					
						
							|  |  |  |   if (type === 'T') { | 
					
						
							|  |  |  |     name = `${name}<T>`; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // adjust the return type for async methods
 | 
					
						
							|  |  |  |   if (member.async) { | 
					
						
							|  |  |  |     if (type === 'void') | 
					
						
							|  |  |  |       type = `Task`; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       type = `Task<${type}>`; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // render args
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   /** @type {string[]} */ | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   let args = []; | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   /** @type {string[]} */ | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |   let explodedArgs = []; | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   /** @type {Map<string, string>} */ | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |   let argTypeMap = new Map([]); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   /** | 
					
						
							| 
									
										
										
										
											2021-04-06 07:21:31 -03:00
										 |  |  |    * | 
					
						
							|  |  |  |    * @param {string} innerArgType | 
					
						
							|  |  |  |    * @param {string} innerArgName | 
					
						
							|  |  |  |    * @param {Documentation.Member} argument | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |    * @param {boolean} isExploded | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |    */ | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   function pushArg(innerArgType, innerArgName, argument, isExploded = false) { | 
					
						
							| 
									
										
										
										
											2021-05-19 16:13:17 -07:00
										 |  |  |     if (innerArgType === 'null') | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |     const requiredPrefix = (argument.required || isExploded) ? "" : "?"; | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |     const requiredSuffix = (argument.required || isExploded) ? "" : " = default"; | 
					
						
							|  |  |  |     var push = `${innerArgType}${requiredPrefix} ${innerArgName}${requiredSuffix}`; | 
					
						
							|  |  |  |     if (isExploded) | 
					
						
							|  |  |  |       explodedArgs.push(push) | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       args.push(push); | 
					
						
							|  |  |  |     argTypeMap.set(push, innerArgName); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * @param {Documentation.Member} arg | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   function processArg(arg) { | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |     if (options.trimRunAndPrefix && arg.name === 'action') | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |     if (arg.name === 'options') { | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       if (options.mode === 'options' || options.mode === 'base') { | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:51 +02:00
										 |  |  |         const optionsType = member.clazz.name + name.replace('<T>', '') + 'Options'; | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |         optionTypes.set(optionsType, arg.type); | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |         args.push(`${optionsType}? options = default`); | 
					
						
							|  |  |  |         argTypeMap.set(`${optionsType}? options = default`, 'options'); | 
					
						
							| 
									
										
										
										
											2021-05-18 07:55:04 -07:00
										 |  |  |         addParamsDoc('options', ['Call options']); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         arg.type.properties.forEach(processArg); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (arg.type.expression === '[string]|[path]') { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |       let argName = toArgumentName(arg.name); | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |       pushArg("string?", `${argName} = default`, arg); | 
					
						
							|  |  |  |       pushArg("string?", `${argName}Path = default`, arg); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       if (arg.spec) { | 
					
						
							|  |  |  |         addParamsDoc(argName, XmlDoc.renderTextOnly(arg.spec, maxDocumentationColumnWidth)); | 
					
						
							|  |  |  |         addParamsDoc(`${argName}Path`, [`Instead of specifying <paramref name="${argName}"/>, gives the file name to load from.`]); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } else if (arg.type.expression === '[boolean]|[Array]<[string]>') { | 
					
						
							|  |  |  |       // HACK: this hurts my brain too
 | 
					
						
							|  |  |  |       // we split this into two args, one boolean, with the logical name
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |       let argName = toArgumentName(arg.name); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       let leftArgType = translateType(arg.type.union[0], parent, (t) => { throw new Error('Not supported'); }); | 
					
						
							|  |  |  |       let rightArgType = translateType(arg.type.union[1], parent, (t) => { throw new Error('Not supported'); }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       pushArg(leftArgType, argName, arg); | 
					
						
							|  |  |  |       pushArg(rightArgType, `${argName}Values`, arg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       addParamsDoc(argName, XmlDoc.renderTextOnly(arg.spec, maxDocumentationColumnWidth)); | 
					
						
							|  |  |  |       addParamsDoc(`${argName}Values`, [`The values to take into account when <paramref name="${argName}"/> is <code>true</code>.`]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |     const argName = toArgumentName(arg.alias || arg.name); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |     const argType = translateType(arg.type, parent, (t) => generateNameDefault(member, argName, t, parent)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (argType === null && arg.type.union) { | 
					
						
							|  |  |  |       // we might have to split this into multiple arguments
 | 
					
						
							|  |  |  |       let translatedArguments = arg.type.union.map(t => translateType(t, parent, (x) => generateNameDefault(member, argName, x, parent))); | 
					
						
							|  |  |  |       if (translatedArguments.includes(null)) | 
					
						
							|  |  |  |         throw new Error('Unexpected null in translated argument types. Aborting.'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       let argDocumentation = XmlDoc.renderTextOnly(arg.spec, maxDocumentationColumnWidth); | 
					
						
							|  |  |  |       for (const newArg of translatedArguments) { | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |         pushArg(newArg, argName, arg, true); // push the exploded arg
 | 
					
						
							|  |  |  |         addParamsDoc(argName, argDocumentation); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |       args.push(arg.required ? 'EXPLODED_ARG' : 'OPTIONAL_EXPLODED_ARG'); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     addParamsDoc(argName, XmlDoc.renderTextOnly(arg.spec, maxDocumentationColumnWidth)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (argName === 'timeout' && argType === 'decimal') { | 
					
						
							|  |  |  |       args.push(`int timeout = 0`); // a special argument, we ignore our convention
 | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pushArg(argType, argName, arg); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |   let modifiers = ''; | 
					
						
							|  |  |  |   if (options.abstract) | 
					
						
							|  |  |  |     modifiers = 'protected abstract '; | 
					
						
							|  |  |  |   if (options.public) | 
					
						
							|  |  |  |     modifiers = 'public '; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 01:17:45 -03:00
										 |  |  |   member.argsArray | 
					
						
							| 
									
										
										
										
											2021-03-23 10:44:50 +01:00
										 |  |  |     .sort((a, b) => b.alias === 'options' ? -1 : 0) //move options to the back to the arguments list
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |     .forEach(processArg); | 
					
						
							| 
									
										
										
										
											2021-05-19 15:49:44 -07:00
										 |  |  |    | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |   let body = ';'; | 
					
						
							|  |  |  |   if (options.mode === 'base') { | 
					
						
							|  |  |  |     // Generate options -> named transition.
 | 
					
						
							|  |  |  |     const tokens = []; | 
					
						
							|  |  |  |     for (const arg of member.argsArray) { | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |       if (arg.name === 'action' && options.trimRunAndPrefix) | 
					
						
							|  |  |  |         continue; | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       if (arg.name !== 'options') { | 
					
						
							|  |  |  |         tokens.push(toArgumentName(arg.name)); | 
					
						
							|  |  |  |         continue; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       for (const opt of arg.type.properties) { | 
					
						
							|  |  |  |         // TODO: use translate type here?
 | 
					
						
							|  |  |  |         if (opt.type.union && !opt.type.union[0].name.startsWith('"') && opt.type.union[0].name !== 'null' && opt.type.expression !== '[string]|[Buffer]') { | 
					
						
							|  |  |  |           // Explode overloads.
 | 
					
						
							|  |  |  |           for (const t of opt.type.union) { | 
					
						
							|  |  |  |             const suffix = toOverloadSuffix(translateType(t, parent)); | 
					
						
							|  |  |  |             tokens.push(`${opt.name}${suffix}: options.${toMemberName(opt)}${suffix}`); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           tokens.push(`${opt.alias || opt.name}: options.${toMemberName(opt)}`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     body = `
 | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |     options ??= new ${member.clazz.name}${name}Options(); | 
					
						
							|  |  |  |     return ${toAsync(name, member.async)}(${tokens.join(', ')}); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  | }`;
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |   if (!explodedArgs.length) { | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |     if (!options.nodocs) { | 
					
						
							|  |  |  |       out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth)); | 
					
						
							|  |  |  |       paramDocs.forEach((value, i) => printArgDoc(i, value, out)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-08-25 14:24:18 +02:00
										 |  |  |     if(member.deprecated) | 
					
						
							|  |  |  |       out.push(`[System.Obsolete]`); | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |     out.push(`${modifiers}${type} ${toAsync(name, member.async)}(${args.join(', ')})${body}`); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     let containsOptionalExplodedArgs = false; | 
					
						
							|  |  |  |     explodedArgs.forEach((explodedArg, argIndex) => { | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       if (!options.nodocs) | 
					
						
							|  |  |  |         out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth)); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |       let overloadedArgs = []; | 
					
						
							|  |  |  |       for (var i = 0; i < args.length; i++) { | 
					
						
							|  |  |  |         let arg = args[i]; | 
					
						
							|  |  |  |         if (arg === 'EXPLODED_ARG' || arg === 'OPTIONAL_EXPLODED_ARG') { | 
					
						
							|  |  |  |           containsOptionalExplodedArgs = arg === 'OPTIONAL_EXPLODED_ARG'; | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |           let argType = argTypeMap.get(explodedArg); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |           if (!options.nodocs) | 
					
						
							|  |  |  |             printArgDoc(argType, paramDocs.get(argType), out); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |           overloadedArgs.push(explodedArg); | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |           let argType = argTypeMap.get(arg); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |           if (!options.nodocs) | 
					
						
							|  |  |  |             printArgDoc(argType, paramDocs.get(argType), out); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |           overloadedArgs.push(arg); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  |       out.push(`${modifiers}${type} ${toAsync(name, member.async)}(${overloadedArgs.join(', ')})${body}`); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |       if (argIndex < explodedArgs.length - 1) | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |         out.push(''); // output a special blank line
 | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If the exploded union arguments are optional, we also output a special
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |     // signature, to help prevent compilation errors with ambiguous overloads.
 | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |     // That particular overload only contains the required arguments, or rather
 | 
					
						
							|  |  |  |     // contains all the arguments *except* the exploded ones.
 | 
					
						
							|  |  |  |     if (containsOptionalExplodedArgs) { | 
					
						
							|  |  |  |       var filteredArgs = args.filter(x => x !== 'OPTIONAL_EXPLODED_ARG'); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       if (!options.nodocs) | 
					
						
							|  |  |  |         out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth)); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |       filteredArgs.forEach((arg) => { | 
					
						
							|  |  |  |         if (arg === 'EXPLODED_ARG') | 
					
						
							|  |  |  |           throw new Error(`Unsupported required union arg combined an optional union inside ${member.name}`); | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |         let argType = argTypeMap.get(arg); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |         if (!options.nodocs) | 
					
						
							|  |  |  |           printArgDoc(argType, paramDocs.get(argType), out); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |       out.push(`${type} ${name}(${filteredArgs.join(', ')})${body}`); | 
					
						
							| 
									
										
										
										
											2021-05-06 10:23:10 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2021-04-06 07:21:31 -03:00
										 |  |  |  * | 
					
						
							|  |  |  |  *  @param {Documentation.Type} type | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |  *  @param {Documentation.Class|Documentation.Type} parent | 
					
						
							| 
									
										
										
										
											2021-05-17 22:28:14 -07:00
										 |  |  |  *  @param {function(Documentation.Type): string} generateNameCallback | 
					
						
							|  |  |  |  *  @param {boolean=} optional | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |  *  @returns {string} | 
					
						
							| 
									
										
										
										
											2021-05-17 22:28:14 -07:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  | function translateType(type, parent, generateNameCallback = t => t.name, optional = false, isReturnType = false) { | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   // a few special cases we can fix automatically
 | 
					
						
							|  |  |  |   if (type.expression === '[null]|[Error]') | 
					
						
							|  |  |  |     return 'void'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (type.union) { | 
					
						
							| 
									
										
										
										
											2021-05-17 21:27:41 -07:00
										 |  |  |     if (type.union[0].name === 'null' && type.union.length === 2) | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  |       return translateType(type.union[1], parent, generateNameCallback, true, isReturnType); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (type.expression === '[string]|[Buffer]') | 
					
						
							|  |  |  |       return `byte[]`; // TODO: make sure we implement extension methods for this!
 | 
					
						
							| 
									
										
										
										
											2021-05-17 21:27:41 -07:00
										 |  |  |     if (type.expression === '[string]|[float]' || type.expression === '[string]|[float]|[boolean]') { | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       console.warn(`${type.name} should be a 'string', but was a ${type.expression}`); | 
					
						
							| 
									
										
										
										
											2021-03-03 19:36:27 +01:00
										 |  |  |       return `string`; | 
					
						
							| 
									
										
										
										
											2021-05-17 21:27:41 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (type.union.length == 2 && type.union[1].name === 'Array' && type.union[1].templates[0].name === type.union[0].name) | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       return `IEnumerable<${type.union[0].name}>`; // an example of this is [string]|[Array]<[string]>
 | 
					
						
							| 
									
										
										
										
											2021-05-17 21:27:41 -07:00
										 |  |  |     if (type.expression === '[float]|"raf"') | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       return `Polling`; // hardcoded because there's no other way to denote this
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 21:27:41 -07:00
										 |  |  |     // Regular primitive enums are named in the markdown.
 | 
					
						
							|  |  |  |     if (type.name) { | 
					
						
							|  |  |  |       enumTypes.set(type.name, type.union.map(t => t.name)); | 
					
						
							| 
									
										
										
										
											2021-05-17 22:28:14 -07:00
										 |  |  |       return optional ? type.name + '?' : type.name; | 
					
						
							| 
									
										
										
										
											2021-05-17 21:27:41 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (type.name === 'Array') { | 
					
						
							|  |  |  |     if (type.templates.length != 1) | 
					
						
							|  |  |  |       throw new Error(`Array (${type.name} from ${parent.name}) has more than 1 dimension. Panic.`); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  |     let innerType = translateType(type.templates[0], parent, generateNameCallback, false, isReturnType); | 
					
						
							|  |  |  |     return isReturnType ? `IReadOnlyList<${innerType}>` : `IEnumerable<${innerType}>`; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (type.name === 'Object') { | 
					
						
							|  |  |  |     // take care of some common cases
 | 
					
						
							|  |  |  |     // TODO: this can be genericized
 | 
					
						
							|  |  |  |     if (type.templates && type.templates.length == 2) { | 
					
						
							| 
									
										
										
										
											2021-04-06 07:21:31 -03:00
										 |  |  |       // get the inner types of both templates, and if they're strings, it's a keyvaluepair string, string,
 | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  |       let keyType = translateType(type.templates[0], parent, generateNameCallback, false, isReturnType); | 
					
						
							|  |  |  |       let valueType = translateType(type.templates[1], parent, generateNameCallback, false, isReturnType); | 
					
						
							| 
									
										
										
										
											2021-05-22 20:05:26 -07:00
										 |  |  |       if (parent.name === 'Request' || parent.name === 'Response') | 
					
						
							|  |  |  |         return `Dictionary<${keyType}, ${valueType}>`; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       return `IEnumerable<KeyValuePair<${keyType}, ${valueType}>>`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((type.name === 'Object') | 
					
						
							|  |  |  |       && !type.properties | 
					
						
							|  |  |  |       && !type.union) { | 
					
						
							|  |  |  |       return 'object'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // this is an additional type that we need to generate
 | 
					
						
							|  |  |  |     let objectName = generateNameCallback(type); | 
					
						
							|  |  |  |     if (objectName === 'Object') { | 
					
						
							|  |  |  |       throw new Error('Object unexpected'); | 
					
						
							|  |  |  |     } else if (type.name === 'Object') { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |       registerModelType(objectName, type); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |     return `${objectName}${optional ? '?' : ''}`; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (type.name === 'Map') { | 
					
						
							|  |  |  |     if (type.templates && type.templates.length == 2) { | 
					
						
							|  |  |  |       // we map to a dictionary
 | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  |       let keyType = translateType(type.templates[0], parent, generateNameCallback, false, isReturnType); | 
					
						
							|  |  |  |       let valueType = translateType(type.templates[1], parent, generateNameCallback, false, isReturnType); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       return `Dictionary<${keyType}, ${valueType}>`; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       throw 'Map has invalid number of templates.'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (type.name === 'function') { | 
					
						
							|  |  |  |     if (type.expression === '[function]' || !type.args) | 
					
						
							|  |  |  |       return 'Action'; // super simple mapping
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let argsList = ''; | 
					
						
							|  |  |  |     if (type.args) { | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  |       let translatedCallbackArguments = type.args.map(t => translateType(t, parent, generateNameCallback, false, isReturnType)); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       if (translatedCallbackArguments.includes(null)) | 
					
						
							|  |  |  |         throw new Error('There was an argument we could not parse. Aborting.'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       argsList = translatedCallbackArguments.join(', '); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!type.returnType) { | 
					
						
							|  |  |  |       // this is an Action
 | 
					
						
							|  |  |  |       return `Action<${argsList}>`; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-09-27 13:25:07 -07:00
										 |  |  |       let returnType = translateType(type.returnType, parent, generateNameCallback, false, isReturnType); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |       if (returnType == null) | 
					
						
							|  |  |  |         throw new Error('Unexpected null as return type.'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return `Func<${argsList}, ${returnType}>`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-16 17:19:30 +01:00
										 |  |  |   if (type.templates) { | 
					
						
							|  |  |  |     // this should mean we have a generic type and we can translate that
 | 
					
						
							|  |  |  |     /** @type {string[]} */ | 
					
						
							|  |  |  |     var types = type.templates.map(template => translateType(template, parent)); | 
					
						
							|  |  |  |     return `${type.name}<${types.join(', ')}>` | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   // there's a chance this is a name we've already seen before, so check
 | 
					
						
							|  |  |  |   // this is also where we map known types, like boolean -> bool, etc.
 | 
					
						
							|  |  |  |   let name = classNameMap.get(type.name) || type.name; | 
					
						
							| 
									
										
										
										
											2021-06-08 12:20:35 -07:00
										 |  |  |   return `${name}${optional ? '?' : ''}`; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2021-04-06 07:21:31 -03:00
										 |  |  |  * @param {string} typeName | 
					
						
							|  |  |  |  * @param {Documentation.Type} type | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  | function registerModelType(typeName, type) { | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   if (['object', 'string', 'int'].includes(typeName)) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2021-05-22 10:13:58 -07:00
										 |  |  |   if (typeName.endsWith('Option')) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   let potentialType = modelTypes.get(typeName); | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |   if (potentialType) { | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  |     // console.log(`Type ${typeName} already exists, so skipping...`);
 | 
					
						
							| 
									
										
										
										
											2021-02-26 18:04:03 +01:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   modelTypes.set(typeName, type); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {string[]} value | 
					
						
							|  |  |  |  * @param {string[]} out | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  | function printArgDoc(name, value, out) { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:16:14 -07:00
										 |  |  |   if (value.length === 1) { | 
					
						
							|  |  |  |     out.push(`/// <param name="${name}">${value}</param>`); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     out.push(`/// <param name="${name}">`); | 
					
						
							|  |  |  |     out.push(...value.map(l => `/// ${l}`)); | 
					
						
							|  |  |  |     out.push(`/// </param>`); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-03-18 01:17:45 -03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-18 23:33:52 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {string} typeName | 
					
						
							|  |  |  |  * @return {string} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function toOverloadSuffix(typeName) { | 
					
						
							|  |  |  |   return toTitleCase(typeName.replace(/[<].*[>]/, '').replace(/[^a-zA-Z]/g, '')); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-26 15:11:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {string} name | 
					
						
							|  |  |  |  * @param {boolean} convert | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function toAsync(name, convert) { | 
					
						
							|  |  |  |   if (!convert) | 
					
						
							|  |  |  |     return name; | 
					
						
							|  |  |  |   if (name.includes('<')) | 
					
						
							|  |  |  |     return name.replace('<', 'Async<'); | 
					
						
							|  |  |  |   return name + 'Async'; | 
					
						
							|  |  |  | } |