export function toScannedMethod( node: babel.ObjectProperty|babel.ObjectMethod|babel.ClassMethod, sourceRange: SourceRange, document: JavaScriptDocument): ScannedMethod { const parsedJsdoc = jsdoc.parseJsdoc(getAttachedComment(node) || ''); const description = parsedJsdoc.description.trim(); const maybeName = getPropertyName(node); const warnings: Warning[] = []; if (!maybeName) { warnings.push(new Warning({ code: 'unknown-method-name', message: `Could not determine name of method from expression of type: ` + `${node.key.type}`, sourceRange: sourceRange, severity: Severity.INFO, parsedDocument: document })); } const value = babel.isObjectProperty(node) ? node.value : node; const result = getClosureType(value, parsedJsdoc, sourceRange, document); const type = result.successful === true ? result.value : 'Function'; const name = maybeName || ''; const scannedMethod: ScannedMethod = { name, type, description, sourceRange, warnings, astNode: {language: 'js', node, containingDocument: document}, jsdoc: parsedJsdoc, privacy: getOrInferPrivacy(name, parsedJsdoc) }; if (value && babel.isFunction(value)) { if (scannedMethod.jsdoc !== undefined) { scannedMethod.return = getReturnFromAnnotation(scannedMethod.jsdoc); } if (scannedMethod.return === undefined) { scannedMethod.return = inferReturnFromBody(value); } scannedMethod.params = (value.params || []).map((nodeParam) => toMethodParam(nodeParam, scannedMethod.jsdoc)); } return scannedMethod; }
export function extractPropertiesFromClassOrObjectBody( node: babel.Class|babel.ObjectExpression, document: JavaScriptDocument): Map<string, ScannedProperty> { const properties = new Map<string, ScannedProperty>(); const accessors = new Map<string, { getter?: babel.ClassMethod | babel.ObjectMethod, setter?: babel.ClassMethod | babel.ObjectMethod }>(); let body; if (babel.isClass(node)) { body = node.body.body; } else { body = node.properties; } for (const member of body) { if (!babel.isMethod(member) && !babel.isObjectProperty(member)) { continue; } const name = getPropertyName(member); if (name === undefined) { continue; } if (babel.isMethod(member) || babel.isFunction(member.value)) { if (babel.isMethod(member) && (member.kind === 'get' || member.kind === 'set')) { let accessor = accessors.get(name); if (!accessor) { accessor = {}; accessors.set(name, accessor); } if (member.kind === 'get') { accessor.getter = member; } else { accessor.setter = member; } } continue; } const astNode = member.key; const sourceRange = document.sourceRangeForNode(member)!; const jsdocAnn = jsdoc.parseJsdoc(getAttachedComment(member) || ''); const detectedType = getClosureType(member.value, jsdocAnn, sourceRange, document); let type: string|undefined = undefined; if (detectedType.successful) { type = detectedType.value; } properties.set(name, { name, astNode: {language: 'js', node: astNode, containingDocument: document}, type, jsdoc: jsdocAnn, sourceRange, description: jsdocAnn ? jsdoc.getDescription(jsdocAnn) : undefined, privacy: getOrInferPrivacy(name, jsdocAnn), warnings: [], readOnly: jsdoc.hasTag(jsdocAnn, 'readonly'), }); } for (const val of accessors.values()) { let getter: ScannedProperty|null = null; let setter: ScannedProperty|null = null; if (val.getter) { const parsedJsdoc = jsdoc.parseJsdoc(getAttachedComment(val.getter) || ''); getter = extractPropertyFromGetterOrSetter(val.getter, parsedJsdoc, document); } if (val.setter) { const parsedJsdoc = jsdoc.parseJsdoc(getAttachedComment(val.setter) || ''); setter = extractPropertyFromGetterOrSetter(val.setter, parsedJsdoc, document); } const prop = getter || setter; if (!prop) { continue; } if (!prop.readOnly) { prop.readOnly = (val.setter === undefined); } properties.set(prop.name, prop); } return properties; }