autouml
Version:
Autogenerate UML diagrams using d2
415 lines (414 loc) • 18.2 kB
JavaScript
"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;