.NET手撸绘制TypeScript类图——上篇

近年来随着交互界面的精细化,TypeScript越来越流行,前端的设计也越来复杂,而类图正是用简单的箭头和方块,反映对象与对象之间关系/依赖的好方式。许多工具都能生成C#类图,有些工具也能生成TypeScript类图,如tsuml,但存在一些局限性。

我们都是.NET开发,为啥不干脆就用.NET撸一个TypeScript类图呢?

说干就干!为了搞到类图,一共分两步走:

  1. 解析.ts文件,生成抽象语法树(AST),并转换为简单的属性方法等对象
  2. 将这个对象绘制出来

本文将分上下两部分,上篇将介绍我移植的一个.NET Standard 2.0TypeScript解析库,下篇将介绍如何将AST转换为真正的图,并实现一些基本的交互。

.ts文件生成抽象语法树

正常来说编译原理挺难的,但好在有人赶在了我的前头 。

TypeScript解析库

我在Github上找到了一个叫TypeScriptAST的项目,它刚好就能将.ts文件转换为AST。但它仅提供了.NET Framework版本。我看了一下实现方式,它是从微软官方的TypeScript仓库按源代码翻译的。其中Parse.cs高达近8000行代码,能把如此巨大的工作翻译完成,可见作者花了不少时间。

我拿了过来,稍微改造了一下,移植到了.NET CoreNuGet包地址为:

https://www.nuget.org/packages/Sdcb.TypeScriptAST/

我移植的这个版本源代码也开放到了Github,使用相同的Apache-2.0协议开源,开源项目链接如下:

https://github.com/sdcb/TypeScriptAST

虽然不知道是不是第一个移植的,但可以确定的是今后.NET Core也能解析TypeScript了:)

注意:官方没有提供TypeScript.NET解析工具,也没建议用.NET,使用ts解析是正常做法,官方的包用起来显然也更有自信——但这就是骚操作,不挑战一下怎么知道极限在哪呢?

简单使用

假如有如下TypeScript代码:

class Class1
{td: number = 3;ts: string = 'hello';doWork(): string {return `${3+this.td}-${this.ts}`;}
}var tc = new Class1();

我们可以使用TypeScriptAST的类进行分析,只需使用TypeScriptAST类:

var ast = new TypeScriptAST(source: tsSourceStringContent);

该类有许多对象,提供了丰富的解析方式,使用如下代码,即可将代码中的类抽出来:

var classAsts = ast.OfKind(SyntaxKind.ClassDeclaration);

由于AST中的属性太多,我们调试时抽重要的显示出来,并转换为JSON

JsonSerializer.Serialize(classAsts.Select(c => new
{c.IdentifierStr,Children = c.Children.Skip(1).Select(x => x.IdentifierStr),
}), new JsonSerializerOptions { WriteIndented = true}).Dump();

结果如下:

[{"IdentifierStr": "Class1","Children": ["td","ts","doWork"]}
]

有了这个,我们即可定义一些类型,用于后续绘制AST

class ClassDef
{public string Name { get; set; }public List<PropertyDef> Properties { get; set; }public List<MethodDef> Methods { get; set; }
}class PropertyDef
{public string Name { get; set; }public bool IsPublic { get; set; }public bool IsStatic { get; set; }public string Type { get; set; }public override string ToString() => (IsPublic ? "+" : "-") + $" {Name}: " + (String.IsNullOrWhiteSpace(Type) ? "any" : Type);
}class MethodDef
{public string Name { get; set; }public bool IsPublic { get; set; }public bool IsStatic { get; set; }public List<ParameterDef> Parameters { get; set; }public string ReturnType { get; set; }public override string ToString() => (IsPublic ? "+" : "-")+ $" {Name}({String.Join(", ", Parameters)})"+ (Name == ".ctor" ? "" : $": {ReturnType}");
}class ParameterDef
{public string Name { get; set; }public string Type { get; set; }public override string ToString() => $"{Name}: {Type}";
}

借助于.NET强大的LINQ,可以将代码写得特别精练,最后可以达到“一行代码*”完成.tsAST的转换:

static Dictionary<string, ClassDef> ParseFiles(IEnumerable<string> files) => files.Select(x => new TypeScriptAST(File.ReadAllText(x), x)).SelectMany(x => x.OfKind(SyntaxKind.ClassDeclaration)).Select(x => new ClassDef{Name = x.OfKind(SyntaxKind.Identifier).FirstOrDefault().GetText(),Properties = x.OfKind(SyntaxKind.PropertyDeclaration).Select(x => new PropertyDef{Name = x.IdentifierStr,IsPublic = x.First.Kind != SyntaxKind.PrivateKeyword,IsStatic = x.OfKind(SyntaxKind.StaticKeyword).Any(),Type = GetType(x),}).ToList(),Methods = x.OfKind(SyntaxKind.Constructor).Concat(x.OfKind(SyntaxKind.MethodDeclaration)).Select(x => new MethodDef{Name = x is ConstructorDeclaration ctor ? ".ctor" : x.IdentifierStr,IsPublic = x.First.Kind != SyntaxKind.PrivateKeyword,IsStatic = x.OfKind(SyntaxKind.StaticKeyword).Any(),Parameters = ((ISignatureDeclaration)x).Parameters.Select(x => new ParameterDef{Name = x.OfKind(SyntaxKind.Identifier).FirstOrDefault().GetText(),Type = GetType(x),}).ToList(),ReturnType = GetReturnType(x),}).ToList(),}).ToDictionary(x => x.Name, v => v);

两个函数稍微提取一下,代码能更精练:

static string GetReturnType(Node node) => node.Children.OfType<TypeNode>().FirstOrDefault()?.GetText();static string GetType(Node node) => node switch
{var x when x.OfKind(SyntaxKind.TypeReference).Any() => x.OfKind(SyntaxKind.TypeReference).First().GetText(),_ => node.Last switch{LiteralExpression literal => literal.Kind.ToString()[..^7].ToLower() switch{"numeric" => "number",var x => x,},var x => x.GetText(),},
};

使用

我对这个ShootR项目进行了分析,分析代码如下:

ParseFiles(Directory.EnumerateFiles(path: @"C:Usersdotnet-loversourcereposShootRShootRShootRClientShips", "*.ts")).Dump();

分析结果:

成功找到了完整的7个类,并将类名字段名字段类型方法名方法参数返回值等信息都解析出来了。本还能做得更详细的,但这些可以留给读者完成。

总结

在本篇我们介绍了如何使用.NET解析TypeScript,并推荐了我移植的一个NuGet包:Sdcb.TypeScriptAST

下篇将在这篇的基础上,介绍如何使用代码将类图渲染出来。

本文所用到的完整代码,可以在我的Github仓库中下载: https://github.com/sdcb/blog-data/tree/master/2019/20191113-ts-uml-with-dotnet

喜欢的朋友 请关注我的微信公众号:【DotNet骚操作】

typescript get方法_.NET手撸绘制TypeScript类图——上篇相关推荐

  1. .NET手撸绘制TypeScript类图——下篇

    .NET手撸绘制TypeScript类图--下篇 在上篇的文章中,我们介绍了如何使用 .NET解析 TypeScript,这篇将介绍如何使用代码将类图渲染出来. 类型定义渲染 不出意外,我们继续使用  ...

  2. .NET手撸绘制TypeScript类图——上篇

    .NET手撸绘制TypeScript类图--上篇 近年来随着交互界面的精细化, TypeScript越来越流行,前端的设计也越来复杂,而 类图正是用简单的箭头和方块,反映对象与对象之间关系/依赖的好方 ...

  3. 呆呆带你手撸一个思维导图-基础篇

    希沃ENOW大前端 公司官网:CVTE(广州视源股份) 团队:CVTE旗下未来教育希沃软件平台中心enow团队 「本文作者:」 前言 你盼世界,我盼望你无bug.Hello 大家好,我是霖呆呆! 哈哈 ...

  4. 快速读懂UML类图,搞懂类之间的6大关系,轻松绘制UML类图

    快速读懂UML类图,搞懂类之间的6大关系,轻松绘制UML类图 前言 一.UML类图简介 二.类之间的六大关系及UML类图 1.依赖关系及UML类图表示 2.泛化关系及UML类图表示 3.实现关系及UM ...

  5. Python自动绘制UML类图、函数调用图(Call Graph)

    文章目录 1. 引言 2. 绘制UML类图 2.1 安装graphviz 2.2 安装pyreverse 2.3 绘制UML类图 3. 绘制函数调用图 3.1 安装graphviz 3.2 安装pyc ...

  6. android 继承类图,Android Studio中绘制UML类图介绍

    Android Studio中绘制UML类图介绍 Android Studio中绘制UML类图介绍 动机 最近开始阅读项目源码,从其中一个模块开始看,奈何大项目中的一个模块,对于萌新而言,也太过于复杂 ...

  7. VSCode绘制UML类图

    目录 1. 简介 1.1 PlantUML 1.2 安装PlantUML 1.3 支持的文件格式 2. 绘制UML类图 类之间的关系​​​​ 关系上的标签​​​​ 添加方法​​​​ 定义可访问性 3. ...

  8. c#小游戏_.NET手撸2048小游戏

    前言 2048是一款益智小游戏,得益于其规则简单,又和 2的倍数有关,因此广为人知,特别是广受程序员的喜爱. 本文将再次使用我自制的"准游戏引擎" FlysEngine,从空白窗口 ...

  9. python能画k线图吗_,求教使用python绘制K线图

    如何用python实现视频关键帧提取并保存为图片 import cv2 vc = cv2.VideoCapture('Test.avi') #读入视频文件 c=1 if vc.isOpened(): ...

最新文章

  1. ckc交易什么意思_限价委托是什么意思?有限制的委托交易
  2. mysql ceill_MYSQL常用函数
  3. 实现MFC中Radio Button组绑定同一变量控制
  4. 取消管理员取得所有权_win7管理员取得所有权批处理 - 卡饭网
  5. 获取一亿数据获取前100个最大值
  6. 有关 AI 人才的 6 个真相
  7. 马云启动“NASA”计划 为未来20年愿景研发核心科技
  8. Android中文API (60) —— DatePicker.OnDateChangedListener
  9. SpringBoot执行流程
  10. nQueen问题java实现
  11. C++ - STL迭代器失效
  12. thymeleaf 使用页面报错_SpringBoot 使用thymeleaf 跳转页面时,总是提示404找不到页面...
  13. Qt QT的I/O流 QT输入输出
  14. java版b2b2c社交电商spring cloud分布式微服务(十)高可用的服务注册中心
  15. 中南大学数字中南、电信校园网无法弹出验证界面解决方法
  16. Datawhale组队学习:数据竞赛(房价预测)课程任务
  17. java jdom 设置第1行_使用JDOM操作XML
  18. 两种重要的数据【逻辑数据模型,概念数据模型】
  19. 财富游戏道具:保险篇
  20. 游戏HTML翻翻乐,大班益智游戏翻翻乐教案

热门文章

  1. php 正则表达式提取出合法的时间_PHP正则表达式核心技术完全详解 第1节
  2. Shiro过滤器配置(ShiroFilterFactoryBean)
  3. 谈谈对 Spring 的理解
  4. Java核心类库篇3——util
  5. mysql rsync复制,mysql复制又同步
  6. Java啤酒生产系统描述_Java描述设计模式(03):工厂方法模式
  7. 自学java去哪找工作比较好_如何自学java?什么程度可以找工作?
  8. 巨一自动化工业机器人_工业机器人和自动化设备连接器
  9. python中计算如何实现_基于python如何实现计算两组数据P值
  10. azure mysql on vnet_管理 VNet 终结点 - Azure 门户 - Azure Database for MySQL | Microsoft Docs