alm
Version:
The best IDE for TypeScript
231 lines (230 loc) • 8.68 kB
JavaScript
"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; });
}