UNPKG

autouml

Version:

Autogenerate UML diagrams using d2

415 lines (414 loc) 18.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeScraper = void 0; var typescript_1 = __importDefault(require("typescript")); var path = __importStar(require("path")); var glob_1 = require("glob"); var wellknown_1 = require("./wellknown"); var helpers_1 = require("./helpers"); var DEFAULT_TYPE = { name: "any", isPrimitive: true, typeLocation: { fileName: "", duplicatedIn: [], namespaceNest: [], }, typeParameters: [], }; function kindToScope(k) { switch (k) { case typescript_1.default.SyntaxKind.ModuleDeclaration: return 2 /* autouml.mapping.ScopeType.NAMESPACE */; case typescript_1.default.SyntaxKind.EnumDeclaration: return 5 /* autouml.mapping.ScopeType.ENUM */; case typescript_1.default.SyntaxKind.InterfaceDeclaration: return 4 /* autouml.mapping.ScopeType.INTERFACE */; case typescript_1.default.SyntaxKind.ClassDeclaration: return 3 /* autouml.mapping.ScopeType.CLASS */; default: return 0 /* autouml.mapping.ScopeType.PROGRAM */; } } function getNameOfScopeable(n) { var _a, _b; var name = "UNKNOWN NAME"; if (typescript_1.default.isClassDeclaration(n)) { var className = (_a = n.name) === null || _a === void 0 ? void 0 : _a.text; // Extract type parameters if the class has them var typeParameters = ((_b = n.typeParameters) === null || _b === void 0 ? void 0 : _b.map(function (tp) { return tp.getText(); }).join(", ")) || ""; if (typeParameters !== "") { typeParameters = "<".concat(typeParameters, ">"); } return "".concat(className).concat(typeParameters); } else { n.forEachChild(function (cn) { if (typescript_1.default.isIdentifier(cn)) { name = cn.getText(); } }); } return name; // } else if (ts.isInterfaceDeclaration(n)) { // return n.name?.text; // } else if (ts.isEnumDeclaration(n)) { // return n.name?.text; // } } function modifierlistToModifierSet(modifiersList) { var tor = new Set(); tor.add(0 /* autouml.mapping.AccessModifier.PUBLIC */); if (modifiersList == undefined) return tor; modifiersList.forEach(function (mod) { switch (mod.kind) { case typescript_1.default.SyntaxKind.PrivateKeyword: tor.add(1 /* autouml.mapping.AccessModifier.PRIVATE */); tor.delete(0 /* autouml.mapping.AccessModifier.PUBLIC */); break; case typescript_1.default.SyntaxKind.ProtectedKeyword: tor.add(2 /* autouml.mapping.AccessModifier.PROTECTED */); break; } }); return tor; } function isUserDefinedType(type) { function hasFlag(type, flag) { return (type.flags & flag) === flag; } var symbol = type.getSymbol(); if (symbol == null) return false; if (wellknown_1.wellKnownTypesSet.has(symbol.name)) { return false; } // Check if the type is an object type with Class or Interface flag if (hasFlag(type, typescript_1.default.TypeFlags.Object)) { var decls = symbol.getDeclarations(); if (!decls) { return false; } return ((typescript_1.default.getCombinedModifierFlags(decls[0]) & typescript_1.default.ModifierFlags .NonPublicAccessibilityModifier) === 0); } // Check if the type is a union or intersection type if (hasFlag(type, typescript_1.default.TypeFlags.UnionOrIntersection)) { var t = type; // You might want to check individual members of the union or intersection return t.types.some(function (memberType) { return isUserDefinedType(memberType); }); } // Check if this is an enum type // if for some reason this returns true... if (hasFlag(type, typescript_1.default.TypeFlags.Enum)) return true; // it's not an enum type if it's an enum literal type if (hasFlag(type, typescript_1.default.TypeFlags.EnumLiteral) && !type.isUnion()) return true; var valueDeclaration = symbol.valueDeclaration; return (valueDeclaration != null && valueDeclaration.kind === typescript_1.default.SyntaxKind.EnumDeclaration); } var TypeScraper = /** @class */ (function () { function TypeScraper(mapper) { this.mapper = mapper; // this.fileMap = new Map<string, boolean>(); this.files = []; // console.log(mapper.getFiles()); for (var _i = 0, _a = mapper.getFiles(); _i < _a.length; _i++) { var p = _a[_i]; var fils = (0, glob_1.globSync)(p, { ignore: "node_modules/**", }); for (var _b = 0, fils_1 = fils; _b < fils_1.length; _b++) { var f = fils_1[_b]; this.files.push(f); // this.fileMap.set(path.resolve(f), true); } } // let mapper = new FileMapper(files); this.program = typescript_1.default.createProgram(this.files, this.mapper.getTSOptions().options); this.checker = this.program.getTypeChecker(); } TypeScraper.prototype.run = function () { var _this = this; this.program .getSourceFiles() .forEach(function (sourceFile) { // let sourceFile = ts.createSourceFile( // fileName, // readFileSync(fileName).toString(), // ts.ScriptTarget.ES2015, // true // ); var fname = path.resolve(sourceFile.fileName); if ( // this.fileMap.get(fname) _this.mapper.getUMLOptions() .includeNodeModules || !(0, helpers_1.from_node_modules)(fname)) { _this.mapper.startScope(path.relative(_this.mapper.getUMLOptions() .baseDir, fname), 1 /* autouml.mapping.ScopeType.FILE */); _this.mapNode(sourceFile); _this.mapper.endScope(); } }); }; TypeScraper.prototype.makeScope = function (node) { this.mapper.startScope(getNameOfScopeable(node), kindToScope(node.kind), this.tsTypeToAutoUMLType(this.checker.getTypeAtLocation(node))); typescript_1.default.forEachChild(node, this.mapNode.bind(this)); this.mapper.endScope(); }; TypeScraper.prototype.locateType = function (i) { var _this = this; var tor = { fileName: "", namespaceNest: [], duplicatedIn: [], }; var isym = i.getSymbol(); if (isym) { // fullname is '"path".A.B.C' // but if the name is local, there is no path var fullName = this.checker.getFullyQualifiedName(isym); // check if the definition is out of this file var fragments = fullName.split('"'); //the declaration is out of this file if (fragments[0] === "") { // tor.fileName = fragments[1]; // note that fragments[1] is missing the file extnesion!!! // isolate the namespace nesting fragments[2] = fragments[2].slice(1); tor.namespaceNest = fragments[2].split("."); } else { // fragments[0] is the entire string unchanged tor.namespaceNest = fragments[0].split("."); } // let currentFileName = path.parse(tor.fileName); // find where this interface is also declared var decls = isym.getDeclarations(); if (decls) { decls.forEach(function (x) { var p = path.relative(_this.mapper.getUMLOptions().baseDir, x.getSourceFile().fileName); if (tor.fileName === "") { tor.fileName = p; } tor.duplicatedIn.push(p); }); } } return tor; }; TypeScraper.prototype.tsTypeToAutoUMLType = function (t) { // clone the default type var tor = JSON.parse(JSON.stringify(DEFAULT_TYPE)); //get the name tor.name = this.checker.typeToString(t); if (isUserDefinedType(t)) { // if (t.isClassOrInterface() || isEnumType(t)) { tor.isPrimitive = false; tor.typeLocation = this.locateType(t); } var targs = t.typeArguments; if (targs) { for (var _i = 0, targs_1 = targs; _i < targs_1.length; _i++) { var arg = targs_1[_i]; tor.typeParameters.push(this.tsTypeToAutoUMLType(arg)); } } return tor; }; TypeScraper.prototype.paramDeclListToIParams = function (p) { var _this = this; var tor = []; tor = p.map(function (x) { var t = DEFAULT_TYPE; var tt = x.type; if (tt) { t = _this.tsTypeToAutoUMLType(_this.checker.getTypeAtLocation(tt)); } return { name: x.name.getText(), type: t, }; }); return tor; }; TypeScraper.prototype.getAllTypesFromCallExpresion = function (callExpr) { var tor = []; for (var _i = 0, _a = callExpr.arguments; _i < _a.length; _i++) { var arg = _a[_i]; var t = this.checker.getTypeAtLocation(arg); if (t.getCallSignatures().length === 0) { tor.push(this.tsTypeToAutoUMLType(t)); } } // tor = callExpr.arguments.map((arg) => { // let t = this.checker.getTypeAtLocation(arg) // if (t.) // this.tsTypeToAutoUMLType( // ) // } var expression = callExpr.expression; if (typescript_1.default.isPropertyAccessExpression(expression) || typescript_1.default.isElementAccessExpression(expression)) { var objectExpression = expression.expression; var objectType = this.checker.getTypeAtLocation(objectExpression); tor.push(this.tsTypeToAutoUMLType(objectType)); } return tor; }; TypeScraper.prototype.mapNode = function (node) { var _this = this; var _a, _b; switch (node.kind) { case typescript_1.default.SyntaxKind.ModuleDeclaration: case typescript_1.default.SyntaxKind.EnumDeclaration: case typescript_1.default.SyntaxKind.InterfaceDeclaration: case typescript_1.default.SyntaxKind.ClassDeclaration: if (typescript_1.default.isClassDeclaration(node) || typescript_1.default.isInterfaceDeclaration(node)) { // get extends and implements data var hc = node.heritageClauses; if (hc) { hc.forEach(function (c) { var relation; if (c.token === typescript_1.default.SyntaxKind.ExtendsKeyword) { relation = 0 /* autouml.mapping .ConnectorType .INHERITS */; } else { relation = 1 /* autouml.mapping .ConnectorType .IMPLEMENTS */; } c.types.forEach(function (t) { _this.mapper.addRelation(_this.tsTypeToAutoUMLType(_this.checker.getTypeAtLocation(c.parent)), relation, _this.tsTypeToAutoUMLType(_this.checker.getTypeFromTypeNode(t))); }); }); } } return this.makeScope(node); /** * NON scope starting constructs */ case typescript_1.default.SyntaxKind.EnumMember: { node.forEachChild(function (cn) { if (cn.kind === typescript_1.default.SyntaxKind.Identifier) { _this.mapper.addEnumMember(cn.getText()); } }); break; } // for interface members case typescript_1.default.SyntaxKind.PropertySignature: { if (typescript_1.default.isPropertySignature(node)) { var t = this.tsTypeToAutoUMLType(this.checker.getTypeAtLocation(node)); this.mapper.addPropertySignature(node.name.getText(), t); this.mapper.addCurrentScopeRelation(3 /* autouml.mapping.ConnectorType .AGGREGATES */, t); } break; } // for interface index signature members case typescript_1.default.SyntaxKind.IndexSignature: { if (typescript_1.default.isIndexSignatureDeclaration(node)) { var t = this.tsTypeToAutoUMLType(this.checker.getTypeAtLocation(node.getChildAt(4))); var name_1 = "[".concat(node .getChildAt(1) .getText() .replace(/ /g, ""), "]"); this.mapper.addPropertySignature(name_1, t); this.mapper.addCurrentScopeRelation(3 /* autouml.mapping.ConnectorType .AGGREGATES */, t); } break; } // for class members case typescript_1.default.SyntaxKind.PropertyDeclaration: { if (typescript_1.default.isPropertyDeclaration(node)) { var t = this.tsTypeToAutoUMLType(this.checker.getTypeAtLocation(node)); this.mapper.addPropertyDeclaration((_a = node.name) === null || _a === void 0 ? void 0 : _a.getText(), modifierlistToModifierSet(node.modifiers), t); this.mapper.addCurrentScopeRelation(3 /* autouml.mapping.ConnectorType .AGGREGATES */, t); } break; } case typescript_1.default.SyntaxKind.Constructor: { if (typescript_1.default.isConstructorDeclaration(node)) { var signature = this.checker.getSignatureFromDeclaration(node); this.mapper.addMethod("constructor", modifierlistToModifierSet(node.modifiers), this.tsTypeToAutoUMLType(this.checker.getReturnTypeOfSignature(signature)), this.paramDeclListToIParams(node.parameters), true); return; } this.mapper.preventNewFunctions(); break; } case typescript_1.default.SyntaxKind.MethodDeclaration: { if (typescript_1.default.isMethodDeclaration(node)) { var signature = this.checker.getSignatureFromDeclaration(node); this.mapper.addMethod((_b = node.name) === null || _b === void 0 ? void 0 : _b.getText(), modifierlistToModifierSet(node.modifiers), this.tsTypeToAutoUMLType(this.checker.getReturnTypeOfSignature(signature)), this.paramDeclListToIParams(node.parameters), false); this.mapper.preventNewFunctions(); break; } } // understand class dependecies case typescript_1.default.SyntaxKind.CallExpression: { if (typescript_1.default.isCallExpression(node)) { var types = this.getAllTypesFromCallExpresion(node); for (var _i = 0, types_1 = types; _i < types_1.length; _i++) { var t = types_1[_i]; if (!t.isPrimitive) { this.mapper.addCurrentScopeRelation(2 /* autouml.mapping .ConnectorType.DEPENDS */, t); } } } } } typescript_1.default.forEachChild(node, this.mapNode.bind(this)); if (typescript_1.default.isMethodDeclaration(node) || typescript_1.default.isConstructorDeclaration(node)) { this.mapper.allowNewFunctions(); } }; return TypeScraper; }()); exports.TypeScraper = TypeScraper;