diff --git a/utils/doclint/api_parser.js b/utils/doclint/api_parser.js index a593872f99..f2f53cbd1f 100644 --- a/utils/doclint/api_parser.js +++ b/utils/doclint/api_parser.js @@ -192,8 +192,7 @@ class ApiParser { method.argsArray.push(options); } p.required = false; - // @ts-ignore - options.type.properties.push(p); + options.type?.properties?.push(p); } } diff --git a/utils/doclint/documentation.js b/utils/doclint/documentation.js index 77b20ca769..e3e92c3ec7 100644 --- a/utils/doclint/documentation.js +++ b/utils/doclint/documentation.js @@ -363,11 +363,6 @@ class Member { this.alias = match[1]; this.overloadIndex = (+match[2]) - 1; } - /** - * Param is true and option false - * @type {Boolean | null} - */ - this.paramOrOption = null; } index() { @@ -384,10 +379,8 @@ class Member { for (const arg of this.argsArray) { this.args.set(arg.name, arg); arg.enclosingMethod = this; - if (arg.name === 'options') { - // @ts-ignore - arg.type.properties.sort((p1, p2) => p1.name.localeCompare(p2.name)); - } + if (arg.name === 'options') + arg.type?.properties?.sort((p1, p2) => p1.name.localeCompare(p2.name)); indexArg(arg); } } @@ -410,11 +403,9 @@ class Member { continue; const overriddenArg = (arg.langs.overrides && arg.langs.overrides[lang]) || arg; overriddenArg.filterForLanguage(lang, options); - // @ts-ignore - if (overriddenArg.name === 'options' && !overriddenArg.type.properties.length) + if (overriddenArg.name === 'options' && !overriddenArg.type?.properties?.length) continue; - // @ts-ignore - overriddenArg.type.filterForLanguage(lang, options); + overriddenArg.type?.filterForLanguage(lang, options); argsArray.push(overriddenArg); } this.argsArray = argsArray; @@ -433,7 +424,6 @@ class Member { const result = new Member(this.kind, { langs: this.langs, since: this.since, deprecated: this.deprecated, discouraged: this.discouraged }, this.name, this.type?.clone(), this.argsArray.map(arg => arg.clone()), this.spec, this.required); result.alias = this.alias; result.async = this.async; - result.paramOrOption = this.paramOrOption; return result; } @@ -526,8 +516,7 @@ class Type { if (!inUnion && (parsedType.union || parsedType.unionName)) { const type = new Type(parsedType.unionName || ''); type.union = []; - // @ts-ignore - for (let t = parsedType; t; t = t.union) { + for (let /** @type {ParsedType | null} */ t = parsedType; t; t = t.union) { const nestedUnion = !!t.unionName && t !== parsedType; type.union.push(Type.fromParsedType(t, !nestedUnion)); if (nestedUnion) @@ -539,7 +528,6 @@ class Type { if (parsedType.args || parsedType.retType) { const type = new Type('function'); type.args = []; - // @ts-ignore for (let t = parsedType.args; t; t = t.next) type.args.push(Type.fromParsedType(t)); type.returnType = parsedType.retType ? Type.fromParsedType(parsedType.retType) : undefined; @@ -549,8 +537,7 @@ class Type { if (parsedType.template) { const type = new Type(parsedType.name); type.templates = []; - // @ts-ignore - for (let t = parsedType.template; t; t = t.next) + for (let /** @type {ParsedType | null} */ t = parsedType.template; t; t = t.next) type.templates.push(Type.fromParsedType(t)); return type; } @@ -613,17 +600,6 @@ class Type { return []; } - /** - * @returns {Member[] | undefined} - */ - sortedProperties() { - if (!this.properties) - return this.properties; - const sortedProperties = [...this.properties]; - sortedProperties.sort((p1, p2) => p1.name.localeCompare(p2.name)); - return sortedProperties; - } - /** * @param {string} lang * @param {LanguageOptions=} options @@ -768,11 +744,10 @@ function patchLinksInText(classOrMember, text, classesMap, membersMap, linkRende let alias = p2; if (classOrMember) { // param/option reference can only be in method or same method parameter comments. - // @ts-ignore - const method = classOrMember.enclosingMethod; - const param = method.argsArray.find(a => a.name === p2); + const method = /** @type {Member} */(classOrMember).enclosingMethod; + const param = method?.argsArray.find(a => a.name === p2); if (!param) - throw new Error(`Referenced parameter ${match} not found in the parent method ${method.name} `); + throw new Error(`Referenced parameter ${match} not found in the parent method ${method?.name} `); alias = param.alias; } return linkRenderer({ param: alias, href }) || match; diff --git a/utils/doclint/generateApiJson.js b/utils/doclint/generateApiJson.js index bb639abbaf..028e983d75 100644 --- a/utils/doclint/generateApiJson.js +++ b/utils/doclint/generateApiJson.js @@ -17,7 +17,6 @@ // @ts-check const path = require('path'); -const Documentation = require('./documentation'); const { parseApi } = require('./api_parser'); const PROJECT_DIR = path.join(__dirname, '..', '..'); @@ -38,14 +37,14 @@ const PROJECT_DIR = path.join(__dirname, '..', '..'); } /** - * @param {Documentation} documentation + * @param {import('./documentation').Documentation} documentation */ function serialize(documentation) { return documentation.classesArray.map(serializeClass); } /** - * @param {Documentation.Class} clazz + * @param {import('./documentation').Class} clazz */ function serializeClass(clazz) { const result = { name: clazz.name, spec: clazz.spec }; @@ -65,7 +64,7 @@ function serializeClass(clazz) { } /** - * @param {Documentation.Member} member + * @param {import('./documentation').Member} member */ function serializeMember(member) { const result = /** @type {any} */ ({ ...member }); @@ -76,14 +75,20 @@ function serializeMember(member) { return result; } +/** + * @param {import('./documentation').Member} arg + */ function serializeProperty(arg) { const result = { ...arg, parent: undefined }; sanitize(result); if (arg.type) - result.type = serializeType(arg.type, arg.name === 'options'); + result.type = serializeType(arg.type); return result; } +/** + * @param {object} result + */ function sanitize(result) { delete result.args; delete result.argsArray; @@ -92,14 +97,13 @@ function sanitize(result) { } /** - * @param {Documentation.Type} type - * @param {boolean} sortProperties + * @param {import('./documentation').Type} type */ -function serializeType(type, sortProperties = false) { +function serializeType(type) { /** @type {any} */ const result = { ...type }; if (type.properties) - result.properties = (sortProperties ? type.sortedProperties() : type.properties).map(serializeProperty); + result.properties = type.properties.map(serializeProperty); if (type.union) result.union = type.union.map(type => serializeType(type)); if (type.templates) diff --git a/utils/generate_types/index.js b/utils/generate_types/index.js index eb1527c80b..ae988ac32c 100644 --- a/utils/generate_types/index.js +++ b/utils/generate_types/index.js @@ -91,7 +91,7 @@ class TypesGenerator { if (!docClass) return ''; handledClasses.add(className); - return this.writeComment(docClass.comment) + '\n'; + return this.writeComment(docClass.comment, '') + '\n'; }, (className, methodName, overloadIndex) => { if (className === 'SuiteFunction' && methodName === '__call') { const cls = this.documentation.classes.get('Test'); @@ -218,7 +218,7 @@ class TypesGenerator { classToString(classDesc) { const parts = []; if (classDesc.comment) { - parts.push(this.writeComment(classDesc.comment)) + parts.push(this.writeComment(classDesc.comment, '')) } const shouldExport = !this.doNotExportClassNames.has(classDesc.name); parts.push(`${shouldExport ? 'export ' : ''}interface ${classDesc.name} ${classDesc.extends ? `extends ${classDesc.extends} ` : ''}{`); @@ -258,7 +258,7 @@ class TypesGenerator { const descriptions = []; for (let [eventName, value] of classDesc.events) { eventName = eventName.toLowerCase(); - const type = this.stringifyComplexType(value && value.type, 'out', ' ', classDesc.name, eventName, 'payload'); + const type = this.stringifyComplexType(value && value.type, 'out', ' ', [classDesc.name, eventName, 'payload']); const argName = this.argNameForType(type); const params = argName ? `${argName}: ${type}` : ''; descriptions.push({ @@ -311,8 +311,8 @@ class TypesGenerator { return parts.join('\n'); } const jsdoc = this.memberJSDOC(member, indent); - const args = this.argsFromMember(member, indent, classDesc.name); - let type = this.stringifyComplexType(member.type, 'out', indent, classDesc.name, member.alias); + const args = this.argsFromMember(member, indent, [classDesc.name]); + let type = this.stringifyComplexType(member.type, 'out', indent, [classDesc.name, member.alias]); if (member.async) type = `Promise<${type}>`; // do this late, because we still want object definitions for overridden types @@ -351,7 +351,12 @@ class TypesGenerator { return this.documentation.classes.get(classDesc.extends); } - writeComment(comment, indent = '') { + /** + * @param {string} comment + * @param {string} indent + * @returns {string} + */ + writeComment(comment, indent) { const parts = []; const out = []; const pushLine = (line) => { @@ -387,26 +392,30 @@ class TypesGenerator { /** * @param {docs.Type|null} type + * @param {'in' | 'out'} direction + * @param {string} indent + * @param {string[]} namespace + * @returns {string} */ - stringifyComplexType(type, direction, indent, ...namespace) { + stringifyComplexType(type, direction, indent, namespace) { if (!type) return 'void'; - return this.stringifySimpleType(type, direction, indent, ...namespace); + return this.stringifySimpleType(type, direction, indent, namespace); } /** * @param {docs.Member[]} properties * @param {string} name - * @param {string=} indent + * @param {string} indent * @returns {string} */ - stringifyObjectType(properties, name, indent = '') { + stringifyObjectType(properties, name, indent) { const parts = []; parts.push(`{`); parts.push(properties.map(member => { const comment = this.memberJSDOC(member, indent + ' '); - const args = this.argsFromMember(member, indent + ' ', name); - const type = this.stringifyComplexType(member.type, 'out', indent + ' ', name, member.name); + const args = this.argsFromMember(member, indent + ' ', [name]); + const type = this.stringifyComplexType(member.type, 'out', indent + ' ', [name, member.name]); return `${comment}${this.nameForProperty(member)}${args}: ${type};`; }).join('\n\n')); parts.push(indent + '}'); @@ -416,14 +425,16 @@ class TypesGenerator { /** * @param {docs.Type | null | undefined} type * @param {'in' | 'out'} direction - * @returns{string} + * @param {string} indent + * @param {string[]} namespace + * @returns {string} */ - stringifySimpleType(type, direction, indent = '', ...namespace) { + stringifySimpleType(type, direction, indent, namespace) { if (!type) return 'void'; if (type.name === 'Object' && type.templates) { - const keyType = this.stringifySimpleType(type.templates[0], direction, indent, ...namespace); - const valueType = this.stringifySimpleType(type.templates[1], direction, indent, ...namespace); + const keyType = this.stringifySimpleType(type.templates[0], direction, indent, namespace); + const valueType = this.stringifySimpleType(type.templates[1], direction, indent, namespace); return `{ [key: ${keyType}]: ${valueType}; }`; } let out = type.name; @@ -434,7 +445,7 @@ class TypesGenerator { if (type.name === 'Object' && type.properties && type.properties.length) { const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join(''); const shouldExport = exported[name]; - const properties = namespace[namespace.length - 1] === 'options' ? type.sortedProperties() : type.properties; + const properties = type.properties; if (!properties) throw new Error(`Object type must have properties`); if (!this.objectDefinitions.some(o => o.name === name)) @@ -448,10 +459,10 @@ class TypesGenerator { if (type.args) { const stringArgs = type.args.map(a => ({ - type: this.stringifySimpleType(a, direction, indent, ...namespace), + type: this.stringifySimpleType(a, direction, indent, namespace), name: a.name.toLowerCase() })); - out = `((${stringArgs.map(({ name, type }) => `${name}: ${type}`).join(', ')}) => ${this.stringifySimpleType(type.returnType, 'out', indent, ...namespace)})`; + out = `((${stringArgs.map(({ name, type }) => `${name}: ${type}`).join(', ')}) => ${this.stringifySimpleType(type.returnType, 'out', indent, namespace)})`; } else if (type.name === 'function') { out = 'Function'; } @@ -460,19 +471,22 @@ class TypesGenerator { if (out === 'Any') return 'any'; if (type.templates) - out += '<' + type.templates.map(t => this.stringifySimpleType(t, direction, indent, ...namespace)).join(', ') + '>'; + out += '<' + type.templates.map(t => this.stringifySimpleType(t, direction, indent, namespace)).join(', ') + '>'; if (type.union) - out = type.union.map(t => this.stringifySimpleType(t, direction, indent, ...namespace)).join('|'); + out = type.union.map(t => this.stringifySimpleType(t, direction, indent, namespace)).join('|'); return out.trim(); } /** * @param {docs.Member} member + * @param {string} indent + * @param {string[]} namespace + * @returns {string} */ - argsFromMember(member, indent, ...namespace) { + argsFromMember(member, indent, namespace) { if (member.kind === 'property') return ''; - return '(' + member.argsArray.map(arg => `${this.nameForProperty(arg)}: ${this.stringifyComplexType(arg.type, 'in', indent, ...namespace, member.alias, arg.alias)}`).join(', ') + ')'; + return '(' + member.argsArray.map(arg => `${this.nameForProperty(arg)}: ${this.stringifyComplexType(arg.type, 'in', indent, [...namespace, member.alias, arg.alias])}`).join(', ') + ')'; } /**