UNPKG

genezio

Version:

Command line utility to interact with Genezio infrastructure.

212 lines (211 loc) 10.3 kB
/* eslint-disable @typescript-eslint/no-var-requires */ import { AstNodeType, MethodKindEnum, SourceType, } from "../../models/genezioModels.js"; import parser from "@babel/parser"; import traverse from "@babel/traverse"; import fs from "fs"; import { isClassDeclaration, isClassMethod, isIdentifier, isAssignmentPattern, isClassProperty, isArrowFunctionExpression, isExportNamedDeclaration, } from "@babel/types"; import { GENEZIO_CLASS_STATIC_METHOD_NOT_SUPPORTED, UserError } from "../../errors.js"; class AstGenerator { async generateAst(input) { const fileData = fs.readFileSync(input.class.path, "utf8"); const result = parser.parse(fileData, { // parse in strict mode and allow module declarations sourceType: "module", plugins: [ // enable jsx and flow syntax "jsx", "flow", "decorators", ], }); let classDefinition = undefined; traverse.default(result, { enter(path) { if (isClassDeclaration(path.node) && isExportNamedDeclaration(path.parent)) { classDefinition = { type: AstNodeType.ClassDefinition, name: path.node.id?.name ?? "undefined", methods: [], path: input.class.path, docString: createDocStringFromBabelComments(path.parent.leadingComments), }; } else if ( // old school function declaration syntax isClassMethod(path.node) && path.node.kind !== "constructor") { if (path.node.static) { throw new UserError(GENEZIO_CLASS_STATIC_METHOD_NOT_SUPPORTED); } const returnType = { type: AstNodeType.AnyLiteral, }; classDefinition?.methods.push({ type: AstNodeType.MethodDefinition, docString: createDocStringFromBabelComments(path.node.leadingComments), name: isIdentifier(path.node.key) ? path.node.key.name : "undefined", static: false, kind: MethodKindEnum.method, returnType: returnType, params: path.node.params.map(function (param) { const astType = { type: AstNodeType.AnyLiteral, }; let optional = false; let defaultValue; switch (param.type) { case "AssignmentPattern": optional = true; switch (param.right.type) { case "StringLiteral": defaultValue = { value: param.right.value, type: AstNodeType.StringLiteral, }; break; case "NumericLiteral": defaultValue = { value: param.right.value.toString(), type: AstNodeType.DoubleLiteral, }; break; case "BooleanLiteral": defaultValue = { value: param.right.value.toString(), type: AstNodeType.BooleanLiteral, }; break; default: defaultValue = undefined; } break; default: defaultValue = undefined; } const astParam = { type: AstNodeType.ParameterDefinition, name: isIdentifier(param) ? param.name : isAssignmentPattern(param) && isIdentifier(param.left) ? param.left.name : "undefined", rawType: "any", paramType: astType, optional, defaultValue, }; return astParam; }), }); } else if ( // arrow function declaration syntax isClassProperty(path.node) && isArrowFunctionExpression(path.node.value)) { const returnType = { type: AstNodeType.AnyLiteral, }; if (path.node.static) { throw new UserError(GENEZIO_CLASS_STATIC_METHOD_NOT_SUPPORTED); } classDefinition?.methods.push({ type: AstNodeType.MethodDefinition, docString: createDocStringFromBabelComments(path.node.leadingComments), name: isIdentifier(path.node.key) ? path.node.key.name : "undefined", static: false, kind: MethodKindEnum.method, returnType: returnType, params: path.node.value.params.map(function (param) { const astType = { type: AstNodeType.AnyLiteral, }; let optional = false; let defaultValue; switch (param.type) { case "AssignmentPattern": optional = true; switch (param.right.type) { case "StringLiteral": defaultValue = { value: param.right.value, type: AstNodeType.StringLiteral, }; break; case "NumericLiteral": defaultValue = { value: param.right.value.toString(), type: AstNodeType.DoubleLiteral, }; break; case "BooleanLiteral": defaultValue = { value: param.right.value.toString(), type: AstNodeType.BooleanLiteral, }; break; default: defaultValue = undefined; } break; default: defaultValue = undefined; } const astParam = { type: AstNodeType.ParameterDefinition, name: isIdentifier(param) ? param.name : isAssignmentPattern(param) && isIdentifier(param.left) ? param.left.name : "undefined", rawType: "any", paramType: astType, optional, defaultValue, }; return astParam; }), }); } }, }); if (classDefinition == undefined) { throw new UserError("No class definition found"); } else { return { program: { body: [classDefinition], originalLanguage: "js", sourceType: SourceType.module, }, }; } } } function createDocStringFromBabelComments(leadingComments) { let docString = undefined; if (leadingComments) { // Iterate over all the comments and retain only the last JSDoc comment for (const comment of leadingComments) { const commentText = extractDocStringFromJsDoc(comment.value); docString = commentText || docString; } } return docString; } function extractDocStringFromJsDoc(jsDoc) { // Only match comments that start with `/**` and end with `*/` (i.e. JSDoc comments) const jsDocRegex = /^\*([\s\S]*)/; const match = jsDoc.match(jsDocRegex); if (match) { // Remove the leading ` * ` from each line return match[1] .replace(/(^|\n)\s*\*\s*/g, "$1") .split("\n") .map((l) => l.trim()) .join("\n"); } return undefined; } const supportedExtensions = ["js"]; export default { supportedExtensions, AstGenerator };