• 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
16import ts from 'typescript';
17import { Code } from '../utils/constant';
18import type {
19  comment, Context, ISourceCodeProcessor, ModifyLogResult, ProcessResult, sourceParser,
20  MethodNodeType, ApiSplitProcessorInterface
21} from './typedef';
22import { ErrorInfo, JSDocModifyType } from './typedef';
23import { CommentHelper, LogResult } from './coreImpls';
24import { LogReportStringUtils } from '../utils/stringUtils';
25
26export class ApiSplitProcessor implements ISourceCodeProcessor, sourceParser.ITransformCallback {
27
28  context?: Context;
29
30  async process(context: Context, content: string): Promise<ProcessResult> {
31    if (!context.getOptions().splitUnionTypeApi) {
32      return { code: Code.OK, content: content };
33    }
34    this.context = context;
35    const sourceParser = context.getSourceParser(content);
36    const sourceFile = sourceParser.transform(this);
37    return {
38      code: Code.OK,
39      content: sourceFile ? sourceParser.printSourceFile(sourceFile) : content
40    };
41  }
42
43  onTransformNode(node: comment.CommentNode): ts.Node | undefined {
44    if (node.astNode === undefined) {
45      return undefined;
46    }
47    const nodeProcessor = nodeProcessorMap.get(node.astNode.kind);
48    return nodeProcessor ? nodeProcessor(node.astNode, this.context) : undefined;
49  }
50}
51
52/**
53 * 订阅事件api拆分工具类
54 */
55class ApiSplitProcessorHelper {
56
57  static splitEventValues(node: ts.Node): string[] {
58    let eventValues: Array<string> = [];
59    if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) {
60      let functionName: string = '';
61      if (node.name && ts.isIdentifier(node.name)) {
62        functionName = node.name.getText();
63      }
64      if ((functionName !== 'on') && (functionName !== 'off')) {
65        return eventValues;
66      }
67      if ((node.parameters[0].type) && (node.parameters[0].type.kind === ts.SyntaxKind.UnionType)) {
68        const types: ts.NodeArray<ts.TypeNode> = (node.parameters[0].type as ts.UnionTypeNode).types;
69        for (let i = 0; i < types.length; i++) {
70          const eventValueNode: ts.TypeNode = types[i];
71          if (eventValueNode.kind === ts.SyntaxKind.LiteralType &&
72            (eventValueNode as ts.LiteralTypeNode).literal.kind === ts.SyntaxKind.StringLiteral) {
73            const eventValue = (eventValueNode as ts.LiteralTypeNode).literal.getText();
74            // 这里获取的eventValue包含了单引号或双引号,因此需要进行截取
75            eventValues.push(eventValue.slice(1, eventValue.length - 1));
76          } else {
77            eventValues = [];
78            break;
79          }
80        }
81      }
82    }
83    return eventValues;
84  }
85
86  static logReportProcess(node: MethodNodeType, context?: Context): void {
87    const apiName: string = `${node.name?.getText()}(${node.parameters[0].type?.getFullText()})`;
88    const description: string = LogReportStringUtils.createErrorInfo(ErrorInfo.EVENT_SUBSCRIPTION_SPLITTION, [`${apiName}`]);
89    const comments: Array<comment.CommentInfo> = CommentHelper.getNodeLeadingComments(node, node.getSourceFile());
90    const modifyLogResult: ModifyLogResult = LogResult.createModifyResult(node, comments, description, context,
91      apiName, JSDocModifyType.EVENT_SUBSCRIPTION_SPLITTION);
92    context?.getLogReporter().addModifyResult(modifyLogResult);
93  }
94
95  static createNewParams(node: MethodNodeType, eventValue: string, typeParam: ts.ParameterDeclaration): ts.ParameterDeclaration[] {
96    const literalTypeNode = ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(eventValue));
97    const newTypeParam: ts.ParameterDeclaration = ts.factory.createParameterDeclaration(typeParam.modifiers,
98      typeParam.dotDotDotToken, typeParam.name, typeParam.questionToken, literalTypeNode, typeParam.initializer);
99    const newParams: Array<ts.ParameterDeclaration> = [];
100    newParams.push(newTypeParam);
101    for (let i = 1; i < node.parameters.length; i++) {
102      const param: ts.ParameterDeclaration = node.parameters[i];
103      const paramNode: ts.ParameterDeclaration = ts.factory.createParameterDeclaration(param.modifiers,
104        param.dotDotDotToken, param.name, param.questionToken, param.type, param.initializer);
105      newParams.push(paramNode);
106    }
107    return newParams;
108  }
109
110  /**
111   * 创建新的FunctionDeclaration类型节点数组
112   * @param node 当前节点
113   * @param eventValues 事件名称数组
114   * @returns 新创建的节点数组
115   */
116  static createFunctionDecNodes(node: ts.FunctionDeclaration, eventValues: Array<string>): Array<ts.FunctionDeclaration> {
117    const newFunctionNodes: Array<ts.FunctionDeclaration> = [];
118    const typeParam: ts.ParameterDeclaration = node.parameters[0];
119    const modifiers: Array<ts.Modifier> = [];
120    node.modifiers?.forEach((modifier: ts.Modifier) => {
121      ts.setEmitFlags(modifier, ts.EmitFlags.NoLeadingComments);
122      const newModifier = ts.factory.createModifier(modifier.kind);
123      modifiers.push(newModifier);
124    });
125    const newModifiers: ts.NodeArray<ts.Modifier> = ts.factory.createNodeArray(modifiers);
126    eventValues.forEach((eventValue: string) => {
127      const newParams: Array<ts.ParameterDeclaration> = ApiSplitProcessorHelper.createNewParams(node, eventValue, typeParam);
128      const newFunctionNode: ts.FunctionDeclaration = ts.factory.createFunctionDeclaration(newModifiers,
129        node.asteriskToken, node.name, node.typeParameters, newParams, node.type, node.body);
130      const comments: Array<comment.CommentInfo> = CommentHelper.getNodeLeadingComments(node, node.getSourceFile());
131      CommentHelper.setComment(newFunctionNode, [CommentHelper.getEmptyLineComment(), ...comments]);
132      newFunctionNodes.push(newFunctionNode);
133    });
134    return newFunctionNodes;
135  }
136
137  /**
138   * 创建新的MethodDeclaration类型节点数组
139   * @param node 当前节点
140   * @param eventValues 事件名称数组
141   * @returns 新创建的节点数组
142   */
143  static createMethodDecNodes(node: ts.MethodDeclaration, eventValues: Array<string>): Array<ts.MethodDeclaration> {
144    const newFunctionNodes: Array<ts.MethodDeclaration> = [];
145    const typeParam: ts.ParameterDeclaration = node.parameters[0];
146    eventValues.forEach((eventValue: string) => {
147      const newParams: Array<ts.ParameterDeclaration> = ApiSplitProcessorHelper.createNewParams(node, eventValue, typeParam);
148      ts.setEmitFlags(node.name, ts.EmitFlags.NoLeadingComments);
149      const newFunctionNode: ts.MethodDeclaration = ts.factory.createMethodDeclaration(node.modifiers, node.asteriskToken,
150        node.name, node.questionToken, node.typeParameters, newParams, node.type, node.body);
151      const comments: Array<comment.CommentInfo> = CommentHelper.getNodeLeadingComments(node, node.getSourceFile());
152      CommentHelper.setComment(newFunctionNode, [CommentHelper.getEmptyLineComment(), ...comments]);
153      newFunctionNodes.push(newFunctionNode);
154    });
155    return newFunctionNodes;
156  }
157
158  /**
159   * 创建新的MethodSignature类型节点数组
160   * @param node 当前节点
161   * @param eventValues 事件名称数组
162   * @returns 新创建的节点数组
163   */
164  static createMethodSigNodes(node: ts.MethodSignature, eventValues: Array<string>): Array<ts.MethodSignature> {
165    const newFunctionNodes: Array<ts.MethodSignature> = [];
166    const typeParam: ts.ParameterDeclaration = node.parameters[0];
167    eventValues.forEach((eventValue: string) => {
168      const newParams: Array<ts.ParameterDeclaration> = ApiSplitProcessorHelper.createNewParams(node, eventValue, typeParam);
169      ts.setEmitFlags(node.name, ts.EmitFlags.NoLeadingComments);
170      const newFunctionNode: ts.MethodSignature = ts.factory.createMethodSignature(node.modifiers, node.name,
171        node.questionToken, node.typeParameters, newParams, node.type);
172      const comments: Array<comment.CommentInfo> = CommentHelper.getNodeLeadingComments(node, node.getSourceFile());
173      CommentHelper.setComment(newFunctionNode, [CommentHelper.getEmptyLineComment(), ...comments]);
174      newFunctionNodes.push(newFunctionNode);
175    });
176
177    return newFunctionNodes;
178  }
179
180  static processSourceFile(node: ts.Node, context: Context | undefined): ts.Node | undefined {
181    const sourceFile: ts.SourceFile = node as ts.SourceFile;
182    const newStatements: Array<ts.Statement> = [];
183    const statements: ts.NodeArray<ts.Statement> = sourceFile.statements;
184    statements.forEach((statement: ts.Statement) => {
185      const eventValues: Array<string> = ApiSplitProcessorHelper.splitEventValues(statement);
186      if (eventValues.length === 0) {
187        newStatements.push(statement);
188      } else {
189        const funcDecNode: ts.FunctionDeclaration = statement as ts.FunctionDeclaration;
190        const newFunctionNodes: Array<ts.FunctionDeclaration> = ApiSplitProcessorHelper.createFunctionDecNodes(funcDecNode, eventValues);
191        newStatements.push(...newFunctionNodes);
192        // 添加报告输出处理逻辑
193        ApiSplitProcessorHelper.logReportProcess(funcDecNode, context);
194      }
195    });
196    if (statements.length === newStatements.length) {
197      return undefined;
198    }
199    return ts.factory.updateSourceFile(sourceFile, newStatements, sourceFile.isDeclarationFile,
200      sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib,
201      sourceFile.libReferenceDirectives);
202  }
203
204  static processModuleDeclaration(node: ts.Node, context: Context | undefined): ts.Node | undefined {
205    const moduleDeclaration: ts.ModuleDeclaration = node as ts.ModuleDeclaration;
206    if (moduleDeclaration.body && moduleDeclaration.body.kind === ts.SyntaxKind.ModuleBlock) {
207      const statements: ts.NodeArray<ts.Statement> = moduleDeclaration.body.statements;
208      const newStatements: Array<ts.Statement> = [];
209      statements.forEach((statement: ts.Statement) => {
210        const eventValues: Array<string> = ApiSplitProcessorHelper.splitEventValues(statement);
211        if (eventValues.length === 0) {
212          newStatements.push(statement);
213        } else {
214          const funcDecNode: ts.FunctionDeclaration = statement as ts.FunctionDeclaration;
215          const newFunctionNodes: Array<ts.FunctionDeclaration> = ApiSplitProcessorHelper.createFunctionDecNodes(funcDecNode, eventValues);
216          newStatements.push(...newFunctionNodes);
217          // 添加报告输出处理逻辑
218          ApiSplitProcessorHelper.logReportProcess(funcDecNode, context);
219        }
220      });
221      if (statements.length === newStatements.length) {
222        return undefined;
223      }
224      const newModuleBlock: ts.ModuleBlock = ts.factory.updateModuleBlock(moduleDeclaration.body, newStatements);
225      return ts.factory.updateModuleDeclaration(moduleDeclaration, moduleDeclaration.modifiers,
226        moduleDeclaration.name, newModuleBlock);
227    }
228    return undefined;
229  }
230
231  static processClassDeclaration(node: ts.Node, context: Context | undefined): ts.Node | undefined {
232    const classDeclaration: ts.ClassDeclaration = node as ts.ClassDeclaration;
233    const members: ts.NodeArray<ts.ClassElement> = classDeclaration.members;
234    const newMembers: Array<ts.ClassElement> = [];
235    members.forEach((member: ts.ClassElement) => {
236      const eventValues: Array<string> = ApiSplitProcessorHelper.splitEventValues(member);
237      if (eventValues.length === 0) {
238        newMembers.push(member);
239      } else {
240        const funcDecNode: ts.MethodDeclaration = member as ts.MethodDeclaration;
241        const newFunctionNodes: Array<ts.MethodDeclaration> = ApiSplitProcessorHelper.createMethodDecNodes(funcDecNode, eventValues);
242        newMembers.push(...newFunctionNodes);
243        // 添加报告输出处理逻辑
244        ApiSplitProcessorHelper.logReportProcess(funcDecNode, context);
245      }
246    });
247    if (members.length === newMembers.length) {
248      return undefined;
249    }
250    return ts.factory.updateClassDeclaration(classDeclaration, classDeclaration.modifiers, classDeclaration.name,
251      classDeclaration.typeParameters, classDeclaration.heritageClauses, newMembers);
252  }
253
254  static processInterfaceDeclaration(node: ts.Node, context: Context | undefined): ts.Node | undefined {
255    const interfaceDeclaration: ts.InterfaceDeclaration = node as ts.InterfaceDeclaration;
256    const members: ts.NodeArray<ts.TypeElement> = interfaceDeclaration.members;
257    const newMembers: Array<ts.TypeElement> = [];
258    members.forEach((member: ts.TypeElement) => {
259      const eventValues: Array<string> = ApiSplitProcessorHelper.splitEventValues(member);
260      if (eventValues.length === 0) {
261        newMembers.push(member);
262      } else {
263        const funcDecNode: ts.MethodSignature = member as ts.MethodSignature;
264        const newFunctionNodes: Array<ts.MethodSignature> = ApiSplitProcessorHelper.createMethodSigNodes(funcDecNode, eventValues);
265        newMembers.push(...newFunctionNodes);
266        // 添加报告输出处理逻辑
267        ApiSplitProcessorHelper.logReportProcess(funcDecNode, context);
268      }
269    });
270    if (members.length === newMembers.length) {
271      return undefined;
272    }
273    return ts.factory.updateInterfaceDeclaration(interfaceDeclaration, interfaceDeclaration.modifiers, interfaceDeclaration.name,
274      interfaceDeclaration.typeParameters, interfaceDeclaration.heritageClauses, newMembers);
275  }
276}
277
278const nodeProcessorMap: Map<ts.SyntaxKind, ApiSplitProcessorInterface> = new Map([
279  [ts.SyntaxKind.SourceFile, ApiSplitProcessorHelper.processSourceFile],
280  [ts.SyntaxKind.ModuleDeclaration, ApiSplitProcessorHelper.processModuleDeclaration],
281  [ts.SyntaxKind.InterfaceDeclaration, ApiSplitProcessorHelper.processInterfaceDeclaration],
282  [ts.SyntaxKind.ClassDeclaration, ApiSplitProcessorHelper.processClassDeclaration]
283]);