• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const path = require('path');
17const fs = require('fs');
18const ts = require('typescript');
19const { parse } = require('comment-parser');
20const { ApiDigestInfo, ApiType, apiDataHelper } = require('./api_data');
21const { isInDirectory } = require('./util');
22
23/**
24 * api 接口摘要信息包装方法
25 *
26 * @param {ts.Node} astNode node节点对象
27 * @param {string} className 类名
28 * @param {ApiType} apiType 方法名
29 * @param {string} rawText 原始文本
30 * @param {VisitExt} ext 扩展参数
31 * @returns {Object}
32 */
33function wrapApiDigestInfo(astNode, className, apiName, apiType, rawText, ext) {
34  const digestInfo = new ApiDigestInfo();
35  const jsdoc = getNodeLeadingJSDoc(astNode);
36  digestInfo.setAstNode(astNode)
37    .setApiType(apiType)
38    .setClassName(className)
39    .setPackageName(ext.packageName)
40    .setRawText(rawText.trim())
41    .setPath(ext.dtsPath)
42    .setApiSignature(astNode, ext.packageName, className)
43    .setApiName(apiName)
44    .setJSdoc(jsdoc ? parseJSDocs(jsdoc) : undefined);
45  return digestInfo;
46}
47
48/**
49 * 接口属性签名信息
50 *
51 * @param {ts.PropertySignature} propertySignature
52 * @param {ApiDigestInfo} parentApiDigest
53 * @param {VisitExt} ext
54 * @returns {Object}
55 */
56function getPropertySignatureInfo(propertySignature, parentApiDigest, ext) {
57  const className = parentApiDigest.getClassName();
58  const rawText = propertySignature.getText();
59  const apiName = propertySignature.name.getText();
60  return wrapApiDigestInfo(propertySignature, className, apiName, ApiType.InterfaceProperty, rawText, ext);
61}
62
63/**
64 *
65 *
66 * @param {ts.MethodDeclaration} methodDeclaration
67 * @param {ApiDigestInfo} parentApiDigest
68 * @param {VisitExt} ext
69 * @returns {Object}
70 */
71function getMethodDeclarationInfo(methodDeclaration, parentApiDigest, ext) {
72  const className = parentApiDigest.getClassName();
73  const rawText = methodDeclaration.getText();
74  const apiName = methodDeclaration.name.getText();
75  return wrapApiDigestInfo(methodDeclaration, className, apiName, ApiType.ClassMethod, rawText, ext);
76}
77
78function getTypeAliasDeclarationInfo(typeAliasDeclaration, parentApiDigest, ext) {
79  const className = parentApiDigest.getClassName();
80  const rawText = typeAliasDeclaration.getText();
81  const apiName = typeAliasDeclaration.name.getText();
82  return wrapApiDigestInfo(typeAliasDeclaration, className, apiName, ApiType.TypeAliasDeclaration, rawText, ext);
83}
84
85function getCallSignature(callSignature, parentApiDigest, ext) {
86  const className = parentApiDigest.getClassName();
87  const rawText = callSignature.getText();
88  const apiName = callSignature.name ? callSignature.name.getText() : className;
89  return wrapApiDigestInfo(callSignature, className, apiName, ApiType.CallSignature, rawText, ext);
90}
91
92/**
93 * 获取方法签名信息
94 *
95 * @param {ts.MethodSignature} methodSignature 方法签名节点
96 * @param {ApiDigestInfo} parentApiDigest
97 * @param {VisitExt} ext
98 * @returns {Object}
99 */
100function getMethodSignatureInfo(methodSignature, parentApiDigest, ext) {
101  const className = parentApiDigest.getClassName();
102  const rawText = methodSignature.getText();
103  const apiName = methodSignature.name.getText();
104  return wrapApiDigestInfo(methodSignature, className, apiName, ApiType.InterfaceMethod, rawText, ext);
105}
106
107/**
108 * 获取接口定义信息
109 *
110 * @param {ts.InterfaceDeclaration} interfaceDec
111 * @param {ApiDigestInfo} parentApiDigest
112 * @param {VisitExt} ext
113 * @returns {Object}
114 */
115function getInterfaceDeclarationInfo(interfaceDec, parentApiDigest, ext) {
116  const className = interfaceDec.name.getText();
117  let rawText = '';
118  if (interfaceDec.modifiers) {
119    interfaceDec.modifiers.forEach((modifier) => {
120      rawText += ` ${modifier.getText()}`;
121    });
122  }
123  rawText += ` interface ${className}`;
124  return wrapApiDigestInfo(interfaceDec, className, className, ApiType.InterfaceType, rawText, ext);
125}
126
127/**
128 * 获取类属性定义
129 *
130 * @param {ts.PropertyDeclaration} property
131 * @param {ApiDigestInfo} parentApiDigest
132 * @param {VisitExt} ext
133 * @returns {Object}
134 */
135function getPropertyDeclarationInfo(property, parentApiDigest, ext) {
136  const className = parentApiDigest.getClassName();
137  const apiName = property.name.getText();
138  return wrapApiDigestInfo(property, className, apiName, ApiType.ClassProperty, property.getText(), ext);
139}
140
141/**
142 * 收集构造方法信息
143 *
144 * @param {ts.ConstructorDeclaration} constructorDec 构造方法定义节点
145 * @param {ApiDigestInfo} parentApiDigest 父类信息
146 * @param {Object} ext 扩展参数
147 * @returns {Object}
148 */
149function getConstructorInfo(constructorDec, parentApiDigest, ext) {
150  const className = parentApiDigest ? parentApiDigest.getClassName() : '';
151  const apiName = 'constructor';
152  return wrapApiDigestInfo(constructorDec, className, apiName, ApiType.Constructor, constructorDec.getText(), ext);
153}
154
155/**
156 * 搜集 Class 定义的信息
157 *
158 * @param {ts.ClassDeclaration} classDec Class定义节点
159 * @param {ApiDigestInfo} parentApiDigest 父类摘要信息
160 * @param {VisitExt} ext 扩展参数
161 * @returns {Object}
162 */
163function getClassDigestInfo(classDec, parentApiDigest, ext) {
164  const className = classDec.name ? classDec.name.text : '';
165  let rawText = '';
166  if (classDec.modifiers) {
167    classDec.modifiers.forEach((modifier) => {
168      rawText += ` ${modifier.getText()}`;
169    });
170  }
171  rawText += ` class ${className}`;
172  return wrapApiDigestInfo(classDec, className, className, ApiType.ClassType, rawText, ext);
173}
174
175/**
176 * 获取 Enum 成员的摘要信息
177 *
178 * @param {ts.EnumMember} enumMember
179 * @param {ApiDigestInfo} parentApiDigest
180 * @param {VisitExt} ext
181 * @returns {Object}
182 */
183function getEnumMemberInfo(enumMember, parentApiDigest, ext) {
184  const className = parentApiDigest ? parentApiDigest.getClassName() : '';
185  const apiName = enumMember.name.getText();
186  return wrapApiDigestInfo(enumMember, className, apiName, ApiType.EnumMember, enumMember.getText(), ext);
187}
188
189/**
190 * 获取 enum 定义的信息
191 *
192 * @param {ts.EnumDeclaration} enumNode Enum节点
193 * @param {ApiDigestInfo} parentApiDigest 父类摘要信息
194 * @param {VisitExt} ext 扩展参数
195 * @returns {Object}
196 */
197function getEnumDigestInfo(enumNode, parentApiDigest, ext) {
198  const className = enumNode.name.getText();
199  let rawText = '';
200  if (enumNode.modifiers) {
201    enumNode.modifiers.forEach((modifier) => {
202      rawText += ` ${modifier.getText()}`;
203    });
204  }
205  rawText += ` enum ${className}`;
206  return wrapApiDigestInfo(enumNode, className, className, ApiType.EnumType, rawText, ext);
207}
208
209/**
210 * 获取 function 定义的API信息
211 *
212 * @param {ts.FunctionDeclaration} fun 方法定义节点
213 * @param {ApiDigestInfo} parentApiDigest 父类的摘要信息
214 * @param {VisitExt} ext 扩展参数
215 * @returns {Object}
216 */
217function getFunctionDigestInfo(fun, parentApiDigest, ext) {
218  const className = parentApiDigest ? parentApiDigest.getClassName() : '';
219  const apiName = fun.name ? fun.name.getText() : '';
220  return wrapApiDigestInfo(fun, className, apiName, ApiType.FunctionType, fun.getText(), ext);
221}
222
223/**
224 * 解析模块定义(namespace)类型。
225 *
226 * @param {ts.ModuleDeclaration} module 模块定义类型对象
227 * @param {VisitExt} ext 扩展参数
228 * @returns {Object}
229 */
230function getModuleDigestInfo(module, parentApiDigest, ext) {
231  const className = module.name.getText();
232  let rawText = '';
233  module.forEachChild((child) => {
234    if (!ts.isModuleBlock(child)) {
235      rawText += ` ${child.getText()}`;
236    }
237  });
238  return wrapApiDigestInfo(module, className, className, ApiType.NamespaceType, rawText, ext);
239}
240
241function getSourceFile(sourceFile, parentApiDigest, ext) {
242  const className = 'sourcefile';
243  const rawText = 'sourcefile';
244  return wrapApiDigestInfo(sourceFile, className, className, ApiType.SourceFile, rawText, ext);
245}
246/**
247 * 所有特定类型API的处理方法集合。
248 */
249const apiDigestMethodMap = new Map([
250  [ts.SyntaxKind.ModuleDeclaration, getModuleDigestInfo],
251  [ts.SyntaxKind.FunctionDeclaration, getFunctionDigestInfo],
252  [ts.SyntaxKind.EnumDeclaration, getEnumDigestInfo],
253  [ts.SyntaxKind.EnumMember, getEnumMemberInfo],
254  [ts.SyntaxKind.ClassDeclaration, getClassDigestInfo],
255  [ts.SyntaxKind.Constructor, getConstructorInfo],
256  [ts.SyntaxKind.PropertyDeclaration, getPropertyDeclarationInfo],
257  [ts.SyntaxKind.InterfaceDeclaration, getInterfaceDeclarationInfo],
258  [ts.SyntaxKind.MethodSignature, getMethodSignatureInfo],
259  [ts.SyntaxKind.PropertySignature, getPropertySignatureInfo],
260  [ts.SyntaxKind.MethodDeclaration, getMethodDeclarationInfo],
261  [ts.SyntaxKind.TypeAliasDeclaration, getTypeAliasDeclarationInfo],
262  [ts.SyntaxKind.CallSignature, getCallSignature],
263  [ts.SyntaxKind.SourceFile, getSourceFile],
264]);
265
266/**
267 * 解析节点的JSDoc信息,可能包含多段。
268 *
269 * @param {string[]} jsdocText
270 * @returns {Array}
271 */
272function parseJSDocs(jsdocText) {
273  return parse(jsdocText);
274}
275
276/**
277 * 获取节点JSDoc的文本
278 *
279 * @param {ts.Node} astNode
280 * @returns {string}
281 */
282function getNodeLeadingJSDoc(astNode) {
283  if (astNode.kind === ts.SyntaxKind.SourceFile) {
284    return '';
285  }
286  const sourceFile = astNode.getSourceFile();
287  const leadingCommentRange = ts.getLeadingCommentRanges(sourceFile.getFullText(), astNode.getFullStart());
288  if (!leadingCommentRange) {
289    return undefined;
290  }
291  let commentText = '';
292  leadingCommentRange.map((range) => {
293    commentText += sourceFile.getFullText().slice(range.pos, range.end);
294    commentText += '\n';
295  });
296  return commentText;
297}
298
299/**
300 * 获取API的摘要信息
301 *
302 * @param {ts.Node} astNode ast 节点对象
303 * @param {VisitExt} ext 可扩展参数对象
304 * @returns {@link ApiDigestInfo}
305 */
306function getApiDigestInfo(astNode, parentApiDigest, ext) {
307  const digestMethod = apiDigestMethodMap.get(astNode.kind);
308  if (digestMethod) {
309    return digestMethod(astNode, parentApiDigest, ext);
310  }
311  return undefined;
312}
313
314/**
315 * 非 API 节点的摘要信息,用于向上追溯父类API信息。
316 *
317 * @param {ts.Node} astNode
318 * @returns {ApiDigestInfo} ApiDigestInfo
319 */
320function getDummyApiDigestInfo(astNode) {
321  const dummyDigestInfo = new ApiDigestInfo();
322  dummyDigestInfo.setAstNode(astNode);
323  return dummyDigestInfo;
324}
325
326/**
327 * 是否需要遍历特定节点类型的子节点。
328 *
329 * @param {ts.Node} astNode
330 * @returns {Boolean} true or false
331 */
332function shouldVisitChildren(astNode) {
333  return ts.isModuleDeclaration(astNode) || ts.isEnumDeclaration(astNode) || ts.isInterfaceDeclaration(astNode) ||
334    ts.isClassDeclaration(astNode) || ts.isModuleBlock(astNode) || ts.isSourceFile(astNode);
335}
336
337/**
338 * 递归遍历AST树的回调方法。
339 *
340 * @param {ts.Node} astNode ast节点对象
341 * @param {Map} apiMap api摘要信息的集合
342 * @param {ApiDigestInfo} parentApiDigest 父节点摘要信息
343 * @param {VisitExt} ext 扩展参数
344 */
345function visitAstNode(astNode, apiMap, parentApiDigest, ext) {
346  let apiDigestInfo = getApiDigestInfo(astNode, parentApiDigest, ext);
347  if (apiDigestInfo) {
348    apiDataHelper.addApiDigestInfo(apiMap, apiDigestInfo);
349  } else {
350    apiDigestInfo = getDummyApiDigestInfo(astNode);
351  }
352  apiDigestInfo.setParent(parentApiDigest);
353  if (shouldVisitChildren(astNode)) {
354    astNode.forEachChild((child) => {
355      visitAstNode(child, apiMap, apiDigestInfo, ext);
356    });
357  }
358}
359
360class VisitExt {
361  constructor(packageName, filePath) {
362    this.packageName = packageName;
363    this.dtsPath = filePath;
364  }
365}
366
367/**
368 * 搜集API的主入口
369 *
370 * @param {string} filePath d.ts路径
371 * @param {string} rootDir sdk根目录
372 * @returns {Map} api集合
373 */
374function collectApi(filePath, rootDir, resultMap) {
375  const apiMap = resultMap ? resultMap : new Map();
376  const apiFileName = path.basename(filePath, '.d.ts');
377  const fileContent = fs.readFileSync(filePath, 'utf-8');
378  const sourceFile = ts.createSourceFile(apiFileName, fileContent, ts.ScriptTarget.ES2017, true);
379  const isArkUI = isInDirectory(path.resolve(rootDir, 'component'), filePath);
380  const packageName = isArkUI ? 'ArkUI' : path.relative(rootDir, filePath);
381  const dtsPath = path.relative(rootDir, filePath).replace(/\\/g, '/');
382  visitAstNode(sourceFile, apiMap, undefined, new VisitExt(packageName, dtsPath));
383  return apiMap;
384}
385
386exports.ApiCollector = {
387  collectApi: collectApi,
388};