UNPKG

ui5plugin-parser

Version:
402 lines (401 loc) 17.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CustomTSClass = void 0; const Hjson = require("hjson"); const path = require("path"); const ts_morph_1 = require("ts-morph"); const ParserPool_1 = require("../../../../parser/pool/ParserPool"); const AbstractCustomClass_1 = require("../AbstractCustomClass"); class CustomTSClass extends AbstractCustomClass_1.AbstractCustomClass { _fillIsAbstract() { throw new Error("Method not implemented."); } _getUIDefine() { throw new Error("Method not implemented."); } // eslint-disable-next-line @typescript-eslint/no-empty-function _fillParentClassName() { } constructor(classDeclaration, parser, typeChecker) { const sourceFile = classDeclaration.getSourceFile(); const className = parser.fileReader.getClassNameFromPath(sourceFile.compilerNode.fileName); super(className ?? "", parser); this.parentClassNameDotNotation = ""; this.methods = []; this.fields = []; this.constructors = []; this.UIDefine = []; this.fsPath = path.resolve(sourceFile.compilerNode.fileName); this.classText = sourceFile.getFullText(); this._sourceFile = sourceFile; this.typeChecker = typeChecker; this.node = classDeclaration; const heritageClause = classDeclaration.compilerNode.heritageClauses?.find(heritage => { return heritage.token == ts_morph_1.ts.SyntaxKind.ExtendsKeyword; }); if (heritageClause) { const parentName = heritageClause.types[0].expression.getText(); const parentImportDeclaration = sourceFile.getImportDeclaration(declaration => { return declaration.getImportClause()?.getDefaultImport()?.getText() === parentName; }); const parentModule = parentImportDeclaration?.getModuleSpecifierValue(); this.parentClassNameDotNotation = (parentModule && this._generateClassNameDotNotationFor(parentModule)) ?? ""; } this._fillUI5Metadata(undefined, false); this._fillClassJSDoc(); } _fillClassJSDoc() { const classJsDocs = this.node.getJsDocs(); this._fillDefaultModelClassName(classJsDocs); this._fillClassDescription(classJsDocs); } _fillDefaultModelClassName(classJsDocs) { const ui5modelDoc = classJsDocs.find(jsDoc => jsDoc.getTags().some(tag => tag.getTagName() === "ui5model")); const tag = ui5modelDoc?.getTags().find(tag => tag.getTagName() === "ui5model"); const comment = tag?.getComment(); if (comment && typeof comment === "string") { const trimmedComment = comment.trim(); this.defaultModelClassName = trimmedComment.substring(1, trimmedComment.length - 1); } } _fillClassDescription(classJsDocs) { const descriptionDoc = classJsDocs.find(jsDoc => jsDoc.getTags().some(tag => tag.getTagName() === "description")); const description = descriptionDoc?.getDescription(); if (description) { this.description = description; } } loadTypes() { this._fillUI5Metadata(undefined, true); } _fillUIDefine() { const importStatements = this._sourceFile.getImportDeclarations(); this.UIDefine = importStatements.map(importStatement => { const modulePath = importStatement.getModuleSpecifier().getLiteralText(); return { path: modulePath, className: modulePath.split("/").pop() ?? "", classNameDotNotation: this._generateClassNameDotNotationFor(modulePath), start: importStatement.getStart(), end: importStatement.getEnd(), acornNode: importStatement }; }); } _generateClassNameDotNotationFor(moduleNameSlash) { let className = moduleNameSlash.replace(/\//g, "."); if (moduleNameSlash?.startsWith(".")) { const manifest = ParserPool_1.default.getManifestForClass(this.className); if (manifest && this.fsPath) { const normalizedManifestPath = path.normalize(manifest.fsPath); const importClassPath = path.resolve(path.dirname(this.fsPath), moduleNameSlash); const relativeToManifest = path.relative(normalizedManifestPath, importClassPath); const pathRelativeToManifestDotNotation = relativeToManifest.split(path.sep).join("."); className = `${manifest.componentName}.${pathRelativeToManifestDotNotation}`; } } if (className.endsWith(".controller")) { className = className.substring(0, className.length - ".controller".length); } return className; } _guessTypeFromUIDefine(typeName) { if (typeName) { const UIDefine = this.UIDefine.find(define => define.className === typeName); return UIDefine?.classNameDotNotation; } } _fillFields(metadata, fillTypes = false) { const fields = this.node.getProperties(); const UIFields = fields.map(field => { const jsDocs = field.getJsDocs(); const ui5IgnoreDoc = jsDocs.some(jsDoc => jsDoc.getTags().some(tag => tag.getTagName() === "ui5ignore")); const positionStart = this._sourceFile.getLineAndColumnAtPos(field.getNameNode().getStart()); const positionEnd = this._sourceFile.getLineAndColumnAtPos(field.getNameNode().getEnd()); const typeNode = field.getTypeNode(); const typeReference = typeNode?.asKind(ts_morph_1.ts.SyntaxKind.TypeReference); const typeQuery = typeNode?.asKind(ts_morph_1.ts.SyntaxKind.TypeQuery); const typeName = typeReference?.getText() ?? typeQuery?.getExprName().getText(); let type = fillTypes ? field.getType().getText() : this._guessTypeFromUIDefine(typeName); if (!fillTypes && !type) { type = this._guessTypeFromInitialization(field); } type = this._modifyType(type ?? "any"); return { ui5ignored: ui5IgnoreDoc, owner: this.className, static: field.isStatic(), abstract: field.isAbstract(), type: type, visibility: field .getModifiers() .find(modifier => [ ts_morph_1.ts.SyntaxKind.ProtectedKeyword, ts_morph_1.ts.SyntaxKind.PrivateKeyword, ts_morph_1.ts.SyntaxKind.PublicKeyword ].includes(modifier.getKind())) ?.getText() ?? "public", name: field.getName(), deprecated: jsDocs.some(jsDoc => ts_morph_1.ts.isJSDocDeprecatedTag(jsDoc.compilerNode)), description: "", isEventHandler: false, node: field, mentionedInTheXMLDocument: false, loc: { start: { line: positionStart.line, column: positionStart.column - 1 }, end: { line: positionEnd.line, column: positionEnd.column - 1 } } }; }); return UIFields; } _guessTypeFromInitialization(field) { let type; const initializer = field.getInitializer(); const newExpression = initializer?.asKind(ts_morph_1.ts.SyntaxKind.NewExpression); const identifier = initializer?.asKind(ts_morph_1.ts.SyntaxKind.Identifier); if (newExpression) { type = newExpression.getExpression().getText(); } else if (identifier) { type = identifier.getText(); } type = this._guessTypeFromUIDefine(type); return type; } _fillMethods(metadata, fillTypes = false) { const methods = this.node.getMethods(); const UIMethods = methods.map(method => { const jsDocs = method.getJsDocs(); const ui5IgnoreDoc = jsDocs.some(jsDoc => jsDoc.getTags().some(tag => tag.getTagName() === "ui5ignore")); const positionStart = this._sourceFile.getLineAndColumnAtPos(method.getNameNode().getStart()); const positionEnd = this._sourceFile.getLineAndColumnAtPos(method.getNameNode().getEnd()); let returnType = fillTypes ? method.getReturnType().getText() : "void"; returnType = this._modifyType(returnType); return { ui5ignored: !!ui5IgnoreDoc, owner: this.className, static: method.isStatic(), abstract: method.isAbstract(), returnType: returnType ?? "void", visibility: method .getModifiers() .find(modifier => [ ts_morph_1.ts.SyntaxKind.ProtectedKeyword, ts_morph_1.ts.SyntaxKind.PrivateKeyword, ts_morph_1.ts.SyntaxKind.PublicKeyword ].includes(modifier.getKind())) ?.getText() ?? "public", params: method.getParameters().map(param => { return { name: param.getName(), type: fillTypes ? this._modifyType(param.getType().getText()) ?? "any" : "any", description: "", isOptional: false }; }), name: method.getName(), position: method.getStart(), deprecated: jsDocs.some(jsDoc => ts_morph_1.ts.isJSDocDeprecatedTag(jsDoc.compilerNode)), description: "", isEventHandler: false, node: method, loc: { start: { line: positionStart.line, column: positionStart.column - 1 }, end: { line: positionEnd.line, column: positionEnd.column - 1 } }, mentionedInTheXMLDocument: false }; }); return UIMethods; } _modifyType(returnType) { if (/import\(".*?"\).default/.test(returnType)) { const path = /(?<=import\(").*?(?="\).default)/.exec(returnType)?.[0]; const UI5Type = path?.startsWith("sap/") ? path.replace(/\//g, ".") : path ? this.parser.fileReader.getClassNameFromPath(path) : undefined; if (UI5Type) { returnType = UI5Type; } } if (/import\(".*?"\)\.[a-zA-Z|$]*/.test(returnType)) { const className = /(?<=import\(".*?"\)\.)[a-zA-Z|$]*/.exec(returnType)?.[0]; if (className) { returnType = className; } } return returnType; } _fillUI5Metadata(classInfo, fillTypes = false) { this._fillUIDefine(); this.methods = this._fillMethods(undefined, fillTypes); this.fields = this._fillFields(undefined, fillTypes); this.constructors = this._fillConstructors(undefined, fillTypes); const metadata = this.node.getProperty("metadata"); const metadataText = metadata?.getInitializer()?.getText(); if (metadataText) { let metadataObject; try { metadataObject = Hjson.parse(metadataText); this.properties = this._fillProperties(metadataObject); this.aggregations = this._fillAggregations(metadataObject); this.events = this._fillEvents(metadataObject); this.associations = this._fillAssociations(metadataObject); this.interfaces = this._fillInterfaces(metadataObject); } catch (error) { console.error(`Couldn't parse metadata: ${error.message}`); return; } } } _fillConstructors(metadata, fillTypes = false) { const constructorDeclarations = this.node.getConstructors(); const constructors = constructorDeclarations.map(constructor => { const jsDocs = constructor.getJsDocs(); const ui5IgnoreDoc = jsDocs.some(jsDoc => jsDoc.getTags().some(tag => tag.getTagName() === "ui5ignore")); const positionStart = this._sourceFile.getLineAndColumnAtPos(constructor.getStart()); const positionEnd = this._sourceFile.getLineAndColumnAtPos(constructor.getEnd()); const method = { ui5ignored: !!ui5IgnoreDoc, owner: this.className, static: false, abstract: false, returnType: fillTypes ? this._modifyType(constructor.getReturnType().getText()) ?? "void" : this.className, visibility: constructor .getModifiers() .find(modifier => [ ts_morph_1.ts.SyntaxKind.ProtectedKeyword, ts_morph_1.ts.SyntaxKind.PrivateKeyword, ts_morph_1.ts.SyntaxKind.PublicKeyword ].includes(modifier.getKind())) ?.getText() ?? "public", params: constructor.getParameters().map(param => { return { name: param.getName(), type: fillTypes ? this._modifyType(param.getType().getText()) ?? "any" : "any", description: "", isOptional: false }; }), name: "constructor", position: constructor.getStart(), deprecated: jsDocs.some(jsDoc => ts_morph_1.ts.isJSDocDeprecatedTag(jsDoc.compilerNode)), description: "", node: constructor, isEventHandler: false, loc: { start: { line: positionStart.line, column: positionStart.column - 1 }, end: { line: positionEnd.line, column: positionEnd.column - 1 } }, mentionedInTheXMLDocument: false }; return method; }); return constructors; } _fillInterfaces(metadata) { const metadataInterfaces = metadata.interfaces; if (!metadataInterfaces) { return []; } return metadataInterfaces; } _fillAggregations(metadata) { const metadataAggregations = metadata.aggregations; if (!metadataAggregations) { return []; } return Object.keys(metadataAggregations).map(sKey => { const aggregation = metadataAggregations[sKey]; return { name: aggregation.name ?? sKey ?? "", type: aggregation.type ?? "any", multiple: aggregation.cardinality === "0..n", singularName: aggregation.singularName ?? aggregation.name ?? sKey ?? "", description: aggregation.deprecation ?? "", visibility: aggregation.visibility ?? "public", default: false }; }); } _fillEvents(metadata) { const metadataEvents = metadata.events; if (!metadataEvents) { return []; } return Object.keys(metadataEvents).map(sKey => { const event = metadataEvents[sKey]; return { name: event.name ?? sKey ?? "", description: "", visibility: event.visibility ?? "public", params: Object.keys(event.parameters ?? {}).map(sKey => { return { name: event.parameters[sKey].name ?? sKey, type: event.parameters[sKey].type }; }) }; }); } _fillProperties(metadata) { const metadataProperties = metadata.properties; if (!metadataProperties) { return []; } const properties = Object.keys(metadataProperties).map(sKey => { const property = metadataProperties[sKey]; return { name: property.name ?? sKey ?? "", type: property.type ?? "any", visibility: property.visibility ?? "public", description: "", typeValues: this.generateTypeValues(property.type ?? "") }; }); return properties; } _fillAssociations(metadata) { const metadataAssociations = metadata.associations; if (!metadataAssociations) { return []; } const associations = Object.keys(metadataAssociations).map(sKey => { const association = metadataAssociations[sKey]; return { name: association.name ?? sKey ?? "", type: association.type ?? "any", multiple: association.cardinality === "0..n", singularName: association.singularName ?? association.name ?? sKey ?? "", description: association.deprecation ?? "", visibility: association.visibility ?? "public" }; }); return associations; } } exports.CustomTSClass = CustomTSClass;