UNPKG

alm

Version:

The best IDE for TypeScript

231 lines (230 loc) 8.68 kB
"use strict"; /** * This is the backend for the uml diagram view */ Object.defineProperty(exports, "__esModule", { value: true }); /** Imports */ var utils = require("../../../../common/utils"); var types = require("../../../../common/types"); var astUtils_1 = require("../modules/astUtils"); /** We just use the *active* project if any */ var activeProject = require("../activeProject"); var getProject = activeProject.GetProject.getCurrentIfAny; /** * Get a uml diagram structure for a file */ function getUmlDiagramForFile(query) { var project = getProject(); var sourceFile = project.getSourceFile(query.filePath); var program = project.languageService.getProgram(); // const modules = tsAnalyzer.collectInformation(program, sourceFile); // console.log(modules); var classes = getClasses({ sourceFile: sourceFile, program: program }); // TODO: sort by base classes (i.e. the class that has the lowest length of extend) return utils.resolve({ classes: classes }); } exports.getUmlDiagramForFile = getUmlDiagramForFile; function getClasses(_a) { var sourceFile = _a.sourceFile, program = _a.program; var result = []; var typeChecker = program.getTypeChecker(); var collect = function (cls) { return result.push(cls); }; collectClasses({ node: sourceFile, collect: collect, sourceFile: sourceFile, program: program, }); return result; } exports.getClasses = getClasses; function collectClasses(config) { var sourceFile = config.sourceFile, program = config.program, collect = config.collect; ts.forEachChild(config.node, function (node) { if (node.kind == ts.SyntaxKind.ClassDeclaration) { collect(transformClass(node, sourceFile, program)); } // Support recursively looking into `a.b.c` style namespaces as well if (node.kind === ts.SyntaxKind.ModuleDeclaration) { collectClasses({ node: node, collect: collect, program: program, sourceFile: sourceFile }); } if (node.kind === ts.SyntaxKind.ModuleBlock) { collectClasses({ node: node, collect: collect, program: program, sourceFile: sourceFile }); } }); } /** * Various Transformers */ function transformClass(node, sourceFile, program) { var result = { name: node.name.text, icon: types.IconType.Class, location: astUtils_1.getDocumentedTypeLocation(sourceFile, node.name.pos), members: [], extends: null, }; if (node.typeParameters) { result.icon = types.IconType.ClassGeneric; } /** Collect members */ ts.forEachChild(node, function (node) { if (node.kind == ts.SyntaxKind.Constructor) { result.members.push(transformClassConstructor(node, sourceFile)); } if (node.kind == ts.SyntaxKind.PropertyDeclaration) { result.members.push(transformClassProperty(node, sourceFile)); } if (node.kind == ts.SyntaxKind.MethodDeclaration) { result.members.push(transformClassMethod(node, sourceFile)); } if (node.kind == ts.SyntaxKind.IndexSignature) { result.members.push(transformClassIndexSignature(node, sourceFile)); } }); /** Collect parent classes */ var classDeclaration = node; if (classDeclaration.heritageClauses) { var extendsClause = classDeclaration.heritageClauses.find(function (c) { return c.token === ts.SyntaxKind.ExtendsKeyword; }); if (extendsClause && extendsClause.types.length > 0) { var expression = extendsClause.types[0]; var typeChecker = program.getTypeChecker(); var symbol = typeChecker.getTypeAtLocation(expression.expression).symbol; if (symbol) { var valueDeclaration = symbol.valueDeclaration; if (valueDeclaration && valueDeclaration.kind === ts.SyntaxKind.ClassDeclaration) { var node_1 = valueDeclaration; var nodeSourceFile = node_1.getSourceFile(); result.extends = transformClass(node_1, nodeSourceFile, program); } } } } /** Figure out any override */ if (result.extends) { /** Collect all parents */ var parents_1 = []; var parent_1 = result.extends; while (parent_1) { parents_1.push(parent_1); parent_1 = parent_1.extends; } /** For each member check if a parent has a member with the same name */ result.members.forEach(function (m) { if (m.name === "constructor") return; // (except for constructor) parents_1.forEach(function (p) { var matchedParentMember = p.members.find(function (pm) { return pm.lifetime === types.UMLClassMemberLifetime.Instance && pm.name === m.name; }); if (matchedParentMember) { m.override = matchedParentMember; } }); }); } return result; } /** Class Constructor */ function transformClassConstructor(node, sourceFile) { var name = "constructor"; var icon = types.IconType.ClassConstructor; var location = astUtils_1.getDocumentedTypeLocation(sourceFile, node.pos); var result = { name: name, icon: icon, location: location, visibility: types.UMLClassMemberVisibility.Public, lifetime: types.UMLClassMemberLifetime.Instance, }; return result; } /** Class Property */ function transformClassProperty(node, sourceFile) { var name = ts.unescapeLeadingUnderscores(ts.getPropertyNameForPropertyNameNode(node.name)); var icon = types.IconType.ClassProperty; var location = astUtils_1.getDocumentedTypeLocation(sourceFile, node.name.getEnd() - 1); var visibility = getVisibility(node); var lifetime = getLifetime(node); var result = { name: name, icon: icon, location: location, visibility: visibility, lifetime: lifetime, }; return result; } /** Class Method */ function transformClassMethod(node, sourceFile) { var name = ts.unescapeLeadingUnderscores(ts.getPropertyNameForPropertyNameNode(node.name)); var icon = types.IconType.ClassMethod; if (node.typeParameters) { icon = types.IconType.ClassMethodGeneric; } var location = astUtils_1.getDocumentedTypeLocation(sourceFile, node.name.getEnd() - 1); var visibility = getVisibility(node); var lifetime = getLifetime(node); var result = { name: name, icon: icon, location: location, visibility: visibility, lifetime: lifetime, }; return result; } /** Class Index Signature */ function transformClassIndexSignature(node, sourceFile) { var name = "Index Signature"; var icon = types.IconType.ClassIndexSignature; var location = astUtils_1.getDocumentedTypeLocation(sourceFile, node.pos); var result = { name: name, icon: icon, location: location, visibility: types.UMLClassMemberVisibility.Public, lifetime: types.UMLClassMemberLifetime.Instance, }; return result; } /** * * General Utilities * */ /** Visibility */ function getVisibility(node) { if (node.modifiers) { if (hasModifierSet(node.modifiers, ts.ModifierFlags.Protected)) { return types.UMLClassMemberVisibility.Protected; } else if (hasModifierSet(node.modifiers, ts.ModifierFlags.Private)) { return types.UMLClassMemberVisibility.Private; } else if (hasModifierSet(node.modifiers, ts.ModifierFlags.Public)) { return types.UMLClassMemberVisibility.Public; } else if (hasModifierSet(node.modifiers, ts.ModifierFlags.Export)) { return types.UMLClassMemberVisibility.Public; } } switch (node.parent.kind) { case ts.SyntaxKind.ClassDeclaration: return types.UMLClassMemberVisibility.Public; case ts.SyntaxKind.ModuleDeclaration: return types.UMLClassMemberVisibility.Private; } return types.UMLClassMemberVisibility.Private; } /** Lifetime */ function getLifetime(node) { if (node.modifiers) { if (hasModifierSet(node.modifiers, ts.ModifierFlags.Static)) { return types.UMLClassMemberLifetime.Static; } } return types.UMLClassMemberLifetime.Instance; } /** Just checks if a flag is set */ function hasModifierSet(modifiers, modifier) { return modifiers.some(function (value) { return (value.flags & modifier) === modifier; }); }