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

0f2447a47368912fe7aeaffa96d36e8f.png

.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();

分析结果:

9d14da9eb907651fed9b14a4ad822cd7.png

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

总结

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

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

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

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

a4161dd6f383d39292a70dae48a4586e.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/564496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring使用AspectJ开发AOP

AspectJ 是一个基于 Java 语言的 AOP 框架&#xff0c;它扩展了 Java 语言。Spring 2.0 以后&#xff0c;新增了对 AspectJ 方式的支持&#xff0c;新版本的 Spring 框架&#xff0c;建议使用 AspectJ 方式开发 AOP。 使用 AspectJ 开发 AOP 通常有两种方式&#xff1a; 基于 …

unicode解码php,PHP解码unicode编码的中文字符

问题背景&#xff1a;晚上在抓取某网站数据&#xff0c;结果在数据包中发现了这么一串编码的数据&#xff1a;"......\u65b0\u6d6a\u5fae\u535a......如何解码unicode编码的字符&#xff1f;【好使】 - PHP网站开发 - 【开源与分享】每日最新博客在置顶博客之后解决方案&a…

python协成_Python协程技术的演进

引言1.1. 存储器山存储器山是 Randal Bryant 在《深入理解计算机系统》一书中提出的概念。基于成本、效率的考量&#xff0c;计算机存储器被设计成多级金字塔结构&#xff0c;塔顶是速度最快、成本最高的 CPU 内部的寄存器(一般几 KB)与高速缓存&#xff0c;塔底是成本最低、速…

matlab特征方程的根,MATLAB 求解特征方程的根轨迹图稳定性分析

原文&#xff1a;http://tecdat.cn/?p3871根轨迹分析在下文中&#xff0c;我们提供了用于根轨迹分析的强大MATLAB命令的简要描述。读者可能想知道为什么当强大的MATLAB命令可用时&#xff0c;教师强调学习手工计算。对于给定的一组开环极点和零点&#xff0c;MATLAB立即绘制根…

python如何把一张图像的所有像素点的值都显示出来_情人节,教你用 Python 向女神表白...

点击上方 “AirPython”&#xff0c;选择 “加为星标”第一时间关注 Python 技术干货&#xff01;2020年&#xff0c;这个看起来如此浪漫的年份&#xff0c;你还是一个人吗&#xff1f;难不成我还能是一条狗&#xff1f;提醒你一下&#xff0c;后天就是 2月14日了。什么&#x…

Spring事务管理接口

Spring 的事务管理是基于 AOP 实现的&#xff0c;而 AOP 是以方法为单位的。Spring 的事务属性分别为传播行为、隔离级别、只读和超时属性&#xff0c;这些属性提供了事务应用的方法和描述策略。 在 Java EE 开发经常采用的分层模式中&#xff0c;Spring 的事务处理位于业务逻…

倒计时小工具_送你3个倒数计日的小程序,让你不再遗忘重要事

每天我们忙于工作&#xff0c;忙于生活&#xff0c;在很多重要事情&#xff0c;重要人的生日&#xff0c;以及重要有意义的日子总会在忙碌中被遗忘&#xff0c;那么这该怎么办呢&#xff1f;别紧张&#xff0c;小编为你带来3个倒数计日的小程序&#xff0c;让你不再遗忘重要事情…

Spring声明式事务管理

Spring 的事务管理有两种方式&#xff1a;一种是传统的编程式事务管理&#xff0c;即通过编写代码实现的事务管理&#xff1b;另一种是基于 AOP 技术实现的声明式事务管理。由于在实际开发中&#xff0c;编程式事务管理很少使用&#xff0c;所以我们只对 Spring 的声明式事务管…

python写算法求最短路径,Python实现迪杰斯特拉算法并生成最短路径的示例代码

def Dijkstra(network,s,d):#迪杰斯特拉算法算s-d的最短路径&#xff0c;并返回该路径和代价print("Start Dijstra Path……")path[]#s-d的最短路径nlen(network)#邻接矩阵维度&#xff0c;即节点个数fmax999w[[0 for i in range(n)]for j in range(n)]#邻接矩阵转化…

无法检查指定的位置是否位于cfs上_打印机知识普及:七大原因导致的打印机无法打印及解决方法...

打印机无法打印的原因有很多&#xff0c;如果我们遇到打印机无法打印应该首先从简单到复杂入手。首先必须排除一些最简单的问题&#xff0c;比如打印机是否正常安装。另外打印机内部是不是已经放置有墨盒以及打印纸等&#xff0c;这些基本问题必须排除&#xff0c;另外还有一个…

Spring基于Annotation实现事务管理

在 Spring 中&#xff0c;除了使用基于 XML 的方式可以实现声明式事务管理以外&#xff0c;还可以通过 Annotation 注解的方式实现声明式事务管理。 使用 Annotation 的方式非常简单&#xff0c;只需要在项目中做两件事&#xff0c;具体如下。 1 在 Spring 容器中注册驱动&…

python扩展,用python扩展列

我试图在python中扩展数据帧的某一列。在r中,我将使用这个函数:在python中,我发现df.pivot_table(),但我刚发现一个错误:pandas.pivot_table(df, values Value, index[Day, Money, Product],columns[Account])^更新结果:数据帧没有更改。它只返回相同的数据文件而不进行扩展。考…

基于matlab的pcm系统仿真_深入理解基于RISC-V ISS Spike的仿真系统:探索Spike,pk和fesrv...

Spike, the RISC-V ISA Simulator, implements a functional model of one or more RISC-V processors.Spike is named after the golden spike used to celebrate the completion of the US transcontinental railway.一些同学初接触RISC-V&#xff0c;总逃脱不了被Rocketchip…

think php 子查询,使用thinkPHP怎么实现一个子查询语句

使用thinkPHP怎么实现一个子查询语句发布时间&#xff1a;2021-01-30 13:31:08来源&#xff1a;亿速云阅读&#xff1a;85作者&#xff1a;Leah这篇文章给大家介绍使用thinkPHP怎么实现一个子查询语句&#xff0c;内容非常详细&#xff0c;感兴趣的小伙伴们可以参考借鉴&#x…

python的三个特性_Python3.9的7个特性

作者|PADHMA编译|VK来源|Analytics Vidhya介绍正如著名作家韦恩•W•戴尔所说&#xff0c;改变你看待事物的方式 你所看待的事物也会改变当Python的新版本问世时&#xff0c;许多人担心向后兼容性问题和其他问题。但是如果你喜欢Python&#xff0c;你一定会对新更新中发布的特性…

C++类的成员变量和成员函数

类可以看做是一种数据类型&#xff0c;它类似于普通的数据类型&#xff0c;但是又有别于普通的数据类型。类这种数据类型是一个包含成员变量和成员函数的集合。 类的成员变量和普通变量一样&#xff0c;也有数据类型和名称&#xff0c;占用固定长度的内存。但是&#xff0c;在…

单机安装oracle,单机安装oracle系统

一、安装oracle6.9操作系统1、硬盘划分为30G(以下分区皆强制为主分区)/boot 200Mswap 4G/ 剩余全部空间安装下一步的步骤就不说了勾选图形选项基本系统--》备份客户端&#xff0c;大系统性能&#xff0c;存储工具&#xff0c;安全工具&#xff0c;性能工具&#xff0c;目录客户…

redis timeout设置多少合适_热水器怎么调温度?一般热水器温度设置多少度比较合适?...

对于热水器的温度设置您知道怎么操作吗&#xff1f;一般情况下电热水器设置多少度比较合适呢&#xff1f;今天蜜罐蚁装修网小编给大家介绍下热水器如何调节温度&#xff0c;以及热水器调节温度在什么范围比较合适。下面请看小编以美的电热水器某个型号产品举例图文讲解热水器调…

C++类成员的访问权限以及类的封装

C通过 public、protected、private 三个关键字来控制成员变量和成员函数的访问权限&#xff0c;它们分别表示公有的、受保护的、私有的&#xff0c;被称为成员访问限定符。所谓访问权限&#xff0c;就是你能不能使用该类中的成员。 C 中的 public、private、protected 只能修饰…

oracle数据库12下载地址,Oracle 数据库和补丁下载地址 12.1.0.2 11.2.0.4 11.2.0.1

Oracle 数据库和补丁下载地址 12.1.0.2 11.2.0.4 11.2.0.1 AIX Linux Windows平台AIX 12.1.0.2 DATABASE DB 数据库软件介质下载地址&#xff1a;ftp://104.236.52.210/aix.ppc64_12102_database_1of2.zipftp://104.236.52.210/aix.ppc64_12102_database_2of2.zipAIX 11.2.0.4…