@easy-electron/ts-parser 1.0.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,743 @@
1
+ 'use strict';
2
+
3
+ const ts = require('typescript');
4
+
5
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
6
+
7
+ const ts__default = /*#__PURE__*/_interopDefaultCompat(ts);
8
+
9
+ class TsParser {
10
+ compilerOptions;
11
+ /**
12
+ * 创建解析器实例
13
+ * @param compilerOptions TypeScript 编译选项,不传则使用默认配置(ES2020 + ESNext + Bundler)
14
+ */
15
+ constructor(compilerOptions) {
16
+ this.compilerOptions = compilerOptions ?? {
17
+ target: ts__default.ScriptTarget.ES2020,
18
+ module: ts__default.ModuleKind.ESNext,
19
+ moduleResolution: ts__default.ModuleResolutionKind.Bundler,
20
+ strict: true
21
+ };
22
+ }
23
+ /**
24
+ * 解析 TS 文件,返回结构化的解析结果
25
+ * @param filePath 文件路径
26
+ * @param content 文件内容(可选,不传则通过 ts.sys 从文件系统读取)
27
+ * @returns 解析后的源文件结构
28
+ * @throws 当文件无法读取时抛出错误
29
+ */
30
+ parse(filePath, content) {
31
+ const sourceText = content ?? ts__default.sys.readFile(filePath);
32
+ if (sourceText === void 0) {
33
+ throw new Error(`Cannot read file: ${filePath}`);
34
+ }
35
+ const sourceFile = ts__default.createSourceFile(
36
+ filePath,
37
+ sourceText,
38
+ this.compilerOptions.target ?? ts__default.ScriptTarget.ES2020,
39
+ true,
40
+ ts__default.ScriptKind.TS
41
+ );
42
+ const result = {
43
+ filePath,
44
+ imports: [],
45
+ exports: [],
46
+ interfaces: [],
47
+ typeAliases: [],
48
+ enums: [],
49
+ classes: [],
50
+ functions: [],
51
+ variables: []
52
+ };
53
+ ts__default.forEachChild(sourceFile, (node) => this.visitNode(node, sourceFile, result));
54
+ return result;
55
+ }
56
+ /**
57
+ * 从源码字符串直接解析,无需文件路径
58
+ * @param source TypeScript 源码字符串
59
+ * @param fileName 虚拟文件名,默认 'anonymous.ts'
60
+ * @returns 解析后的源文件结构
61
+ */
62
+ parseSource(source, fileName = "anonymous.ts") {
63
+ return this.parse(fileName, source);
64
+ }
65
+ // ==================== 节点遍历 ====================
66
+ /**
67
+ * 遍历源文件的顶层节点,根据节点类型分发到对应的解析方法
68
+ * @param node 当前遍历的 AST 节点
69
+ * @param sourceFile 源文件对象(用于获取文本)
70
+ * @param result 解析结果容器,各解析方法会将结果写入其中
71
+ */
72
+ visitNode(node, sourceFile, result) {
73
+ if (ts__default.isImportDeclaration(node)) {
74
+ result.imports.push(this.parseImport(node, sourceFile));
75
+ } else if (ts__default.isExportDeclaration(node)) {
76
+ result.exports.push(this.parseExport(node, sourceFile));
77
+ } else if (ts__default.isInterfaceDeclaration(node)) {
78
+ result.interfaces.push(this.parseInterface(node, sourceFile));
79
+ } else if (ts__default.isTypeAliasDeclaration(node)) {
80
+ result.typeAliases.push(this.parseTypeAlias(node, sourceFile));
81
+ } else if (ts__default.isEnumDeclaration(node)) {
82
+ result.enums.push(this.parseEnum(node, sourceFile));
83
+ } else if (ts__default.isClassDeclaration(node)) {
84
+ result.classes.push(this.parseClass(node, sourceFile));
85
+ } else if (ts__default.isFunctionDeclaration(node)) {
86
+ result.functions.push(this.parseFunctionDeclaration(node, sourceFile));
87
+ } else if (ts__default.isVariableStatement(node)) {
88
+ this.parseVariableStatement(node, sourceFile, result);
89
+ } else if (ts__default.isExportAssignment(node)) {
90
+ result.defaultExport = this.parseDefaultExport(node, sourceFile);
91
+ }
92
+ }
93
+ // ==================== 导入解析 ====================
94
+ /**
95
+ * 解析 import 声明节点
96
+ *
97
+ * 支持以下导入形式:
98
+ * - 默认导入: `import Foo from 'module'`
99
+ * - 命名导入: `import { Foo, Bar as Baz } from 'module'`
100
+ * - 命名空间导入: `import * as ns from 'module'`
101
+ * - 类型导入: `import type { Foo } from 'module'`
102
+ * - 混合导入: `import Foo, { Bar } from 'module'`
103
+ *
104
+ * @param node import 声明 AST 节点
105
+ * @param sourceFile 源文件对象
106
+ * @returns 导入声明信息
107
+ */
108
+ parseImport(node, sourceFile) {
109
+ const moduleSpecifier = node.moduleSpecifier.text;
110
+ const isTypeOnly = node.importClause?.isTypeOnly ?? false;
111
+ let defaultImport;
112
+ const namedImports = [];
113
+ let namespaceImport;
114
+ if (node.importClause) {
115
+ if (node.importClause.name) {
116
+ defaultImport = node.importClause.name.text;
117
+ }
118
+ const bindings = node.importClause.namedBindings;
119
+ if (bindings) {
120
+ if (ts__default.isNamedImports(bindings)) {
121
+ for (const element of bindings.elements) {
122
+ namedImports.push({
123
+ name: (element.propertyName ?? element.name).text,
124
+ alias: element.propertyName ? element.name.text : void 0,
125
+ isTypeOnly: element.isTypeOnly
126
+ });
127
+ }
128
+ } else if (ts__default.isNamespaceImport(bindings)) {
129
+ namespaceImport = bindings.name.text;
130
+ }
131
+ }
132
+ }
133
+ return {
134
+ moduleSpecifier,
135
+ isTypeOnly,
136
+ defaultImport,
137
+ namedImports,
138
+ namespaceImport,
139
+ text: node.getText(sourceFile)
140
+ };
141
+ }
142
+ // ==================== 导出解析 ====================
143
+ /**
144
+ * 解析 export 声明节点(不含 export default)
145
+ *
146
+ * 支持以下导出形式:
147
+ * - 命名导出: `export { Foo, Bar as Baz }`
148
+ * - 重导出: `export { Foo } from 'module'`
149
+ * - 全量重导出: `export * from 'module'`
150
+ * - 类型导出: `export type { Foo }`
151
+ *
152
+ * @param node export 声明 AST 节点
153
+ * @param sourceFile 源文件对象
154
+ * @returns 导出声明信息
155
+ */
156
+ parseExport(node, sourceFile) {
157
+ const namedExports = [];
158
+ const isTypeOnly = node.isTypeOnly;
159
+ const moduleSpecifier = node.moduleSpecifier ? node.moduleSpecifier.text : void 0;
160
+ if (node.exportClause && ts__default.isNamedExports(node.exportClause)) {
161
+ for (const element of node.exportClause.elements) {
162
+ namedExports.push({
163
+ name: (element.propertyName ?? element.name).text,
164
+ alias: element.propertyName ? element.name.text : void 0,
165
+ isTypeOnly: element.isTypeOnly
166
+ });
167
+ }
168
+ }
169
+ return {
170
+ namedExports,
171
+ moduleSpecifier,
172
+ isTypeOnly,
173
+ isDefault: false,
174
+ text: node.getText(sourceFile)
175
+ };
176
+ }
177
+ // ==================== 接口解析 ====================
178
+ /**
179
+ * 解析接口声明节点
180
+ *
181
+ * 提取接口的名称、泛型参数、继承关系、成员属性及其类型、可选性、JSDoc 注释等。
182
+ *
183
+ * @param node 接口声明 AST 节点
184
+ * @param sourceFile 源文件对象
185
+ * @returns 接口信息
186
+ */
187
+ parseInterface(node, sourceFile) {
188
+ const typeParameters = this.getTypeParameters(node.typeParameters);
189
+ const extendsClause = [];
190
+ if (node.heritageClauses) {
191
+ for (const clause of node.heritageClauses) {
192
+ if (clause.token === ts__default.SyntaxKind.ExtendsKeyword) {
193
+ for (const type of clause.types) {
194
+ extendsClause.push(type.getText(sourceFile));
195
+ }
196
+ }
197
+ }
198
+ }
199
+ const members = [];
200
+ for (const member of node.members) {
201
+ if (ts__default.isPropertySignature(member) || ts__default.isMethodSignature(member)) {
202
+ members.push({
203
+ name: member.name?.getText(sourceFile) ?? "",
204
+ type: member.type?.getText(sourceFile) ?? "any",
205
+ isOptional: !!member.questionToken,
206
+ comment: this.getJsDoc(member, sourceFile)
207
+ });
208
+ }
209
+ }
210
+ return {
211
+ name: node.name.text,
212
+ isExported: this.hasExportModifier(node),
213
+ typeParameters,
214
+ extends: extendsClause,
215
+ members,
216
+ comment: this.getJsDoc(node, sourceFile),
217
+ text: node.getText(sourceFile)
218
+ };
219
+ }
220
+ // ==================== 类型别名解析 ====================
221
+ /**
222
+ * 解析类型别名声明节点
223
+ *
224
+ * 如: `type Foo<T> = Bar | Baz`
225
+ *
226
+ * @param node 类型别名声明 AST 节点
227
+ * @param sourceFile 源文件对象
228
+ * @returns 类型别名信息
229
+ */
230
+ parseTypeAlias(node, sourceFile) {
231
+ return {
232
+ name: node.name.text,
233
+ isExported: this.hasExportModifier(node),
234
+ typeParameters: this.getTypeParameters(node.typeParameters),
235
+ type: node.type.getText(sourceFile),
236
+ comment: this.getJsDoc(node, sourceFile),
237
+ text: node.getText(sourceFile)
238
+ };
239
+ }
240
+ // ==================== 枚举解析 ====================
241
+ /**
242
+ * 解析枚举声明节点
243
+ *
244
+ * 支持普通枚举和 const 枚举,提取每个成员的名称、初始值和注释。
245
+ *
246
+ * @param node 枚举声明 AST 节点
247
+ * @param sourceFile 源文件对象
248
+ * @returns 枚举信息
249
+ */
250
+ parseEnum(node, sourceFile) {
251
+ const members = node.members.map((member) => ({
252
+ name: member.name.getText(sourceFile),
253
+ value: member.initializer?.getText(sourceFile),
254
+ comment: this.getJsDoc(member, sourceFile)
255
+ }));
256
+ return {
257
+ name: node.name.text,
258
+ isExported: this.hasExportModifier(node),
259
+ isConst: node.modifiers?.some((m) => m.kind === ts__default.SyntaxKind.ConstKeyword) ?? false,
260
+ members,
261
+ comment: this.getJsDoc(node, sourceFile),
262
+ text: node.getText(sourceFile)
263
+ };
264
+ }
265
+ // ==================== 类解析 ====================
266
+ /**
267
+ * 解析类声明节点
268
+ *
269
+ * 提取类的名称、泛型参数、继承/实现关系、是否抽象类,
270
+ * 以及所有成员(属性、方法、构造函数、getter/setter)。
271
+ *
272
+ * @param node 类声明 AST 节点
273
+ * @param sourceFile 源文件对象
274
+ * @returns 类信息
275
+ */
276
+ parseClass(node, sourceFile) {
277
+ const typeParameters = this.getTypeParameters(node.typeParameters);
278
+ let extendsClause;
279
+ const implementsClause = [];
280
+ if (node.heritageClauses) {
281
+ for (const clause of node.heritageClauses) {
282
+ if (clause.token === ts__default.SyntaxKind.ExtendsKeyword) {
283
+ extendsClause = clause.types[0]?.getText(sourceFile);
284
+ } else if (clause.token === ts__default.SyntaxKind.ImplementsKeyword) {
285
+ for (const type of clause.types) {
286
+ implementsClause.push(type.getText(sourceFile));
287
+ }
288
+ }
289
+ }
290
+ }
291
+ const members = [];
292
+ for (const member of node.members) {
293
+ const parsed = this.parseClassMember(member, sourceFile);
294
+ if (parsed) members.push(parsed);
295
+ }
296
+ return {
297
+ name: node.name?.text ?? "",
298
+ isExported: this.hasExportModifier(node),
299
+ isAbstract: node.modifiers?.some((m) => m.kind === ts__default.SyntaxKind.AbstractKeyword) ?? false,
300
+ typeParameters,
301
+ extends: extendsClause,
302
+ implements: implementsClause,
303
+ members,
304
+ comment: this.getJsDoc(node, sourceFile),
305
+ text: node.getText(sourceFile)
306
+ };
307
+ }
308
+ /**
309
+ * 解析单个类成员节点
310
+ *
311
+ * 根据成员类型(构造函数、属性、方法、getter、setter)提取对应信息,
312
+ * 包括访问修饰符(public/private/protected)、static、readonly、abstract 等修饰符。
313
+ *
314
+ * @param member 类成员 AST 节点
315
+ * @param sourceFile 源文件对象
316
+ * @returns 类成员信息,无法识别的成员类型返回 null
317
+ */
318
+ parseClassMember(member, sourceFile) {
319
+ const accessibility = this.getAccessibility(member);
320
+ const modifiers = ts__default.canHaveModifiers(member) ? ts__default.getModifiers(member) : void 0;
321
+ const isStatic = modifiers?.some((m) => m.kind === ts__default.SyntaxKind.StaticKeyword) ?? false;
322
+ const isReadonly = modifiers?.some((m) => m.kind === ts__default.SyntaxKind.ReadonlyKeyword) ?? false;
323
+ const isAbstract = modifiers?.some((m) => m.kind === ts__default.SyntaxKind.AbstractKeyword) ?? false;
324
+ if (ts__default.isConstructorDeclaration(member)) {
325
+ return {
326
+ name: "constructor",
327
+ kind: "constructor",
328
+ accessibility,
329
+ isStatic: false,
330
+ isReadonly: false,
331
+ isAbstract: false,
332
+ isOptional: false,
333
+ parameters: this.parseParameters(member.parameters, sourceFile),
334
+ comment: this.getJsDoc(member, sourceFile)
335
+ };
336
+ }
337
+ if (ts__default.isPropertyDeclaration(member)) {
338
+ return {
339
+ name: member.name.getText(sourceFile),
340
+ kind: "property",
341
+ accessibility,
342
+ isStatic,
343
+ isReadonly,
344
+ isAbstract,
345
+ isOptional: !!member.questionToken,
346
+ type: member.type?.getText(sourceFile),
347
+ comment: this.getJsDoc(member, sourceFile)
348
+ };
349
+ }
350
+ if (ts__default.isMethodDeclaration(member)) {
351
+ return {
352
+ name: member.name.getText(sourceFile),
353
+ kind: "method",
354
+ accessibility,
355
+ isStatic,
356
+ isReadonly: false,
357
+ isAbstract,
358
+ isOptional: !!member.questionToken,
359
+ type: member.type?.getText(sourceFile),
360
+ parameters: this.parseParameters(member.parameters, sourceFile),
361
+ comment: this.getJsDoc(member, sourceFile)
362
+ };
363
+ }
364
+ if (ts__default.isGetAccessorDeclaration(member)) {
365
+ return {
366
+ name: member.name.getText(sourceFile),
367
+ kind: "getter",
368
+ accessibility,
369
+ isStatic,
370
+ isReadonly: false,
371
+ isAbstract,
372
+ isOptional: false,
373
+ type: member.type?.getText(sourceFile),
374
+ comment: this.getJsDoc(member, sourceFile)
375
+ };
376
+ }
377
+ if (ts__default.isSetAccessorDeclaration(member)) {
378
+ return {
379
+ name: member.name.getText(sourceFile),
380
+ kind: "setter",
381
+ accessibility,
382
+ isStatic,
383
+ isReadonly: false,
384
+ isAbstract,
385
+ isOptional: false,
386
+ parameters: this.parseParameters(member.parameters, sourceFile),
387
+ comment: this.getJsDoc(member, sourceFile)
388
+ };
389
+ }
390
+ return null;
391
+ }
392
+ // ==================== 函数解析 ====================
393
+ /**
394
+ * 解析函数声明节点
395
+ *
396
+ * 如: `function foo<T>(a: string): Promise<T> { ... }`
397
+ * 提取函数名、泛型参数、参数列表、返回类型、async/generator 标记等。
398
+ *
399
+ * @param node 函数声明 AST 节点
400
+ * @param sourceFile 源文件对象
401
+ * @returns 函数信息
402
+ */
403
+ parseFunctionDeclaration(node, sourceFile) {
404
+ return {
405
+ name: node.name?.text ?? "",
406
+ isExported: this.hasExportModifier(node),
407
+ isDefault: node.modifiers?.some((m) => m.kind === ts__default.SyntaxKind.DefaultKeyword) ?? false,
408
+ isAsync: node.modifiers?.some((m) => m.kind === ts__default.SyntaxKind.AsyncKeyword) ?? false,
409
+ isGenerator: !!node.asteriskToken,
410
+ typeParameters: this.getTypeParameters(node.typeParameters),
411
+ parameters: this.parseParameters(node.parameters, sourceFile),
412
+ returnType: node.type?.getText(sourceFile),
413
+ comment: this.getJsDoc(node, sourceFile),
414
+ text: node.getText(sourceFile)
415
+ };
416
+ }
417
+ // ==================== 变量解析 ====================
418
+ /**
419
+ * 解析变量声明语句
420
+ *
421
+ * 对每个变量声明进行分类处理:
422
+ * - 如果初始化表达式是箭头函数/函数表达式,归入 functions
423
+ * - 如果初始化表达式是函数调用,解析为 CallExpressionInfo
424
+ * - 其他情况作为普通变量处理
425
+ *
426
+ * @param node 变量声明语句 AST 节点
427
+ * @param sourceFile 源文件对象
428
+ * @param result 解析结果容器
429
+ */
430
+ parseVariableStatement(node, sourceFile, result) {
431
+ const isExported = this.hasExportModifier(node);
432
+ const flags = node.declarationList.flags;
433
+ const declarationKind = flags & ts__default.NodeFlags.Const ? "const" : flags & ts__default.NodeFlags.Let ? "let" : "var";
434
+ for (const decl of node.declarationList.declarations) {
435
+ const name = decl.name.getText(sourceFile);
436
+ const type = decl.type?.getText(sourceFile);
437
+ const comment = this.getJsDoc(node, sourceFile);
438
+ if (decl.initializer && this.isFunctionLike(decl.initializer)) {
439
+ result.functions.push(this.parseFunctionExpression(name, decl.initializer, isExported, sourceFile, comment));
440
+ continue;
441
+ }
442
+ let initializer;
443
+ if (decl.initializer) {
444
+ if (ts__default.isCallExpression(decl.initializer)) {
445
+ initializer = this.parseCallExpression(decl.initializer, sourceFile);
446
+ } else {
447
+ initializer = decl.initializer.getText(sourceFile);
448
+ }
449
+ }
450
+ result.variables.push({
451
+ name,
452
+ declarationKind,
453
+ isExported,
454
+ type,
455
+ initializer,
456
+ comment,
457
+ text: node.getText(sourceFile)
458
+ });
459
+ }
460
+ }
461
+ /**
462
+ * 判断节点是否为函数类表达式(箭头函数或函数表达式)
463
+ * @param node AST 节点
464
+ * @returns 是否为函数类表达式
465
+ */
466
+ isFunctionLike(node) {
467
+ return ts__default.isArrowFunction(node) || ts__default.isFunctionExpression(node);
468
+ }
469
+ /**
470
+ * 将箭头函数/函数表达式变量解析为 FunctionInfo
471
+ *
472
+ * 如: `const foo = async (a: string) => { ... }`
473
+ *
474
+ * @param name 变量名(即函数名)
475
+ * @param node 函数表达式 AST 节点
476
+ * @param isExported 是否导出
477
+ * @param sourceFile 源文件对象
478
+ * @param comment JSDoc 注释
479
+ * @returns 函数信息
480
+ */
481
+ parseFunctionExpression(name, node, isExported, sourceFile, comment) {
482
+ if (ts__default.isArrowFunction(node) || ts__default.isFunctionExpression(node)) {
483
+ return {
484
+ name,
485
+ isExported,
486
+ isDefault: false,
487
+ isAsync: node.modifiers?.some((m) => m.kind === ts__default.SyntaxKind.AsyncKeyword) ?? false,
488
+ isGenerator: ts__default.isFunctionExpression(node) ? !!node.asteriskToken : false,
489
+ typeParameters: this.getTypeParameters(node.typeParameters),
490
+ parameters: this.parseParameters(node.parameters, sourceFile),
491
+ returnType: node.type?.getText(sourceFile),
492
+ comment,
493
+ text: node.getText(sourceFile)
494
+ };
495
+ }
496
+ return {
497
+ name,
498
+ isExported,
499
+ isDefault: false,
500
+ isAsync: false,
501
+ isGenerator: false,
502
+ typeParameters: [],
503
+ parameters: [],
504
+ comment,
505
+ text: node.getText(sourceFile)
506
+ };
507
+ }
508
+ // ==================== 表达式解析 ====================
509
+ /**
510
+ * 解析函数调用表达式
511
+ *
512
+ * 如: `defineIpc('test', { ... })` 或 `createApp<Config>(options)`
513
+ * 提取被调用函数名、泛型参数、实参列表(递归解析每个参数)。
514
+ *
515
+ * @param node 函数调用表达式 AST 节点
516
+ * @param sourceFile 源文件对象
517
+ * @returns 函数调用表达式信息
518
+ */
519
+ parseCallExpression(node, sourceFile) {
520
+ const callee = node.expression.getText(sourceFile);
521
+ const typeArguments = node.typeArguments?.map((t) => t.getText(sourceFile)) ?? [];
522
+ const args = node.arguments.map((arg) => this.parseArgument(arg, sourceFile));
523
+ return {
524
+ callee,
525
+ typeArguments,
526
+ arguments: args,
527
+ text: node.getText(sourceFile)
528
+ };
529
+ }
530
+ /**
531
+ * 解析表达式参数,递归处理各种字面量和复合类型
532
+ *
533
+ * 支持的参数类型:
534
+ * - 字符串字面量 → kind: 'string'
535
+ * - 数字字面量 → kind: 'number'
536
+ * - 布尔字面量 → kind: 'boolean'
537
+ * - 对象字面量 → kind: 'object'(递归解析属性)
538
+ * - 数组字面量 → kind: 'array'(递归解析元素)
539
+ * - 函数/箭头函数 → kind: 'function'
540
+ * - 标识符引用 → kind: 'identifier'
541
+ * - 其他表达式 → kind: 'other'
542
+ *
543
+ * @param node 参数 AST 节点
544
+ * @param sourceFile 源文件对象
545
+ * @returns 参数信息
546
+ */
547
+ parseArgument(node, sourceFile) {
548
+ if (ts__default.isStringLiteral(node) || ts__default.isNoSubstitutionTemplateLiteral(node)) {
549
+ return { kind: "string", value: node.text, text: node.getText(sourceFile) };
550
+ }
551
+ if (ts__default.isNumericLiteral(node)) {
552
+ return { kind: "number", value: Number(node.text), text: node.getText(sourceFile) };
553
+ }
554
+ if (node.kind === ts__default.SyntaxKind.TrueKeyword) {
555
+ return { kind: "boolean", value: true, text: "true" };
556
+ }
557
+ if (node.kind === ts__default.SyntaxKind.FalseKeyword) {
558
+ return { kind: "boolean", value: false, text: "false" };
559
+ }
560
+ if (ts__default.isObjectLiteralExpression(node)) {
561
+ const properties = [];
562
+ for (const prop of node.properties) {
563
+ if (ts__default.isPropertyAssignment(prop)) {
564
+ properties.push({
565
+ name: prop.name.getText(sourceFile),
566
+ value: this.parseArgument(prop.initializer, sourceFile),
567
+ comment: this.getJsDoc(prop, sourceFile)
568
+ });
569
+ } else if (ts__default.isShorthandPropertyAssignment(prop)) {
570
+ properties.push({
571
+ name: prop.name.getText(sourceFile),
572
+ value: { kind: "identifier", value: prop.name.getText(sourceFile), text: prop.name.getText(sourceFile) },
573
+ comment: this.getJsDoc(prop, sourceFile)
574
+ });
575
+ } else if (ts__default.isMethodDeclaration(prop)) {
576
+ const funcInfo = this.parseMethodAsFunctionExpression(prop, sourceFile);
577
+ properties.push({
578
+ name: prop.name.getText(sourceFile),
579
+ value: { kind: "function", function: funcInfo, text: prop.getText(sourceFile) },
580
+ comment: this.getJsDoc(prop, sourceFile)
581
+ });
582
+ }
583
+ }
584
+ return { kind: "object", properties, text: node.getText(sourceFile) };
585
+ }
586
+ if (ts__default.isArrayLiteralExpression(node)) {
587
+ const elements = node.elements.map((el) => this.parseArgument(el, sourceFile));
588
+ return { kind: "array", elements, text: node.getText(sourceFile) };
589
+ }
590
+ if (ts__default.isArrowFunction(node) || ts__default.isFunctionExpression(node)) {
591
+ return {
592
+ kind: "function",
593
+ function: this.parseFunctionExpressionInfo(node, sourceFile),
594
+ text: node.getText(sourceFile)
595
+ };
596
+ }
597
+ if (ts__default.isIdentifier(node)) {
598
+ return { kind: "identifier", value: node.text, text: node.getText(sourceFile) };
599
+ }
600
+ return { kind: "other", text: node.getText(sourceFile) };
601
+ }
602
+ /**
603
+ * 将箭头函数/函数表达式节点解析为 FunctionExpressionInfo
604
+ * @param node 箭头函数或函数表达式 AST 节点
605
+ * @param sourceFile 源文件对象
606
+ * @returns 函数表达式信息(含参数、返回类型、函数体)
607
+ */
608
+ parseFunctionExpressionInfo(node, sourceFile) {
609
+ return {
610
+ isAsync: node.modifiers?.some((m) => m.kind === ts__default.SyntaxKind.AsyncKeyword) ?? false,
611
+ parameters: this.parseParameters(node.parameters, sourceFile),
612
+ returnType: node.type?.getText(sourceFile),
613
+ bodyText: node.body?.getText(sourceFile)
614
+ };
615
+ }
616
+ /**
617
+ * 将对象字面量中的方法声明解析为 FunctionExpressionInfo
618
+ *
619
+ * 如: `{ getData() { ... } }` 中的 getData 方法
620
+ *
621
+ * @param node 方法声明 AST 节点
622
+ * @param sourceFile 源文件对象
623
+ * @returns 函数表达式信息
624
+ */
625
+ parseMethodAsFunctionExpression(node, sourceFile) {
626
+ return {
627
+ isAsync: node.modifiers?.some((m) => m.kind === ts__default.SyntaxKind.AsyncKeyword) ?? false,
628
+ parameters: this.parseParameters(node.parameters, sourceFile),
629
+ returnType: node.type?.getText(sourceFile),
630
+ bodyText: node.body?.getText(sourceFile)
631
+ };
632
+ }
633
+ // ==================== 默认导出解析 ====================
634
+ /**
635
+ * 解析默认导出表达式(export default ...)
636
+ *
637
+ * 根据导出表达式的类型分类:
638
+ * - 函数调用: `export default defineIpc(...)` → kind: 'call'
639
+ * - 标识符: `export default router` → kind: 'identifier'
640
+ * - 对象字面量: `export default { ... }` → kind: 'object'
641
+ * - 其他表达式 → kind: 'other'
642
+ *
643
+ * @param node 默认导出赋值 AST 节点
644
+ * @param sourceFile 源文件对象
645
+ * @returns 默认导出信息
646
+ */
647
+ parseDefaultExport(node, sourceFile) {
648
+ const expr = node.expression;
649
+ if (ts__default.isCallExpression(expr)) {
650
+ return {
651
+ kind: "call",
652
+ call: this.parseCallExpression(expr, sourceFile),
653
+ text: node.getText(sourceFile)
654
+ };
655
+ }
656
+ if (ts__default.isIdentifier(expr)) {
657
+ return {
658
+ kind: "identifier",
659
+ identifier: expr.text,
660
+ text: node.getText(sourceFile)
661
+ };
662
+ }
663
+ if (ts__default.isObjectLiteralExpression(expr)) {
664
+ return {
665
+ kind: "object",
666
+ text: node.getText(sourceFile)
667
+ };
668
+ }
669
+ return {
670
+ kind: "other",
671
+ text: node.getText(sourceFile)
672
+ };
673
+ }
674
+ // ==================== 工具方法 ====================
675
+ /**
676
+ * 解析函数/方法的参数列表
677
+ * @param params 参数声明节点数组
678
+ * @param sourceFile 源文件对象
679
+ * @returns 参数信息列表(含名称、类型、可选性、剩余参数、默认值)
680
+ */
681
+ parseParameters(params, sourceFile) {
682
+ return params.map((param) => ({
683
+ name: param.name.getText(sourceFile),
684
+ type: param.type?.getText(sourceFile),
685
+ isOptional: !!param.questionToken,
686
+ isRest: !!param.dotDotDotToken,
687
+ defaultValue: param.initializer?.getText(sourceFile)
688
+ }));
689
+ }
690
+ /**
691
+ * 提取泛型参数列表的文本表示
692
+ * @param typeParams 泛型参数声明节点数组
693
+ * @returns 泛型参数文本列表,如 ['T', 'K extends string']
694
+ */
695
+ getTypeParameters(typeParams) {
696
+ return typeParams?.map((tp) => tp.getText()) ?? [];
697
+ }
698
+ /**
699
+ * 检查节点是否具有 export 修饰符
700
+ * @param node AST 节点
701
+ * @returns 是否导出
702
+ */
703
+ hasExportModifier(node) {
704
+ const modifiers = ts__default.canHaveModifiers(node) ? ts__default.getModifiers(node) : void 0;
705
+ return modifiers?.some((m) => m.kind === ts__default.SyntaxKind.ExportKeyword) ?? false;
706
+ }
707
+ /**
708
+ * 获取类成员的访问修饰符
709
+ * @param node 类成员 AST 节点
710
+ * @returns 访问修饰符(public/private/protected),无显式修饰符时返回 undefined
711
+ */
712
+ getAccessibility(node) {
713
+ const modifiers = ts__default.canHaveModifiers(node) ? ts__default.getModifiers(node) : void 0;
714
+ if (!modifiers) return void 0;
715
+ if (modifiers.some((m) => m.kind === ts__default.SyntaxKind.PrivateKeyword)) return "private";
716
+ if (modifiers.some((m) => m.kind === ts__default.SyntaxKind.ProtectedKeyword)) return "protected";
717
+ if (modifiers.some((m) => m.kind === ts__default.SyntaxKind.PublicKeyword)) return "public";
718
+ return void 0;
719
+ }
720
+ /**
721
+ * 提取节点的 JSDoc 注释
722
+ *
723
+ * 通过 ts.getLeadingCommentRanges 获取节点前的注释块,
724
+ * 只返回以 `/**` 开头的 JSDoc 风格注释,忽略普通行注释和块注释。
725
+ *
726
+ * @param node AST 节点
727
+ * @param sourceFile 源文件对象
728
+ * @returns JSDoc 注释文本(含 `/**` 和 `*​/`),无 JSDoc 时返回 undefined
729
+ */
730
+ getJsDoc(node, sourceFile) {
731
+ const fullText = sourceFile.getFullText();
732
+ const ranges = ts__default.getLeadingCommentRanges(fullText, node.getFullStart());
733
+ if (!ranges || ranges.length === 0) return void 0;
734
+ const lastRange = ranges[ranges.length - 1];
735
+ const commentText = fullText.slice(lastRange.pos, lastRange.end);
736
+ if (commentText.startsWith("/**")) {
737
+ return commentText;
738
+ }
739
+ return void 0;
740
+ }
741
+ }
742
+
743
+ exports.TsParser = TsParser;