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 {
17  isClassDeclaration,
18  isEnumDeclaration,
19  isExportDeclaration,
20  isFunctionDeclaration,
21  isIdentifier,
22  isImportEqualsDeclaration,
23  isInterfaceDeclaration,
24  isModuleBlock,
25  isModuleDeclaration,
26  isTypeAliasDeclaration,
27  isVariableStatement
28} from 'typescript';
29import type { ModuleDeclaration, Node, SourceFile, ModuleBody } from 'typescript';
30import { getExportKeyword } from '../common/commonUtils';
31import { getClassDeclaration } from './classDeclaration';
32import type { ClassEntity } from './classDeclaration';
33import { getEnumDeclaration } from './enumDeclaration';
34import type { EnumEntity } from './enumDeclaration';
35import { getFunctionDeclaration } from './functionDeclaration';
36import type { FunctionEntity } from './functionDeclaration';
37import { getExportDeclaration, getModuleImportEqual } from './importAndExportDeclaration';
38import type { ImportEuqalEntity } from './importAndExportDeclaration';
39import { getInterfaceDeclaration } from './interfaceDeclaration';
40import type { InterfaceEntity } from './interfaceDeclaration';
41import { getTypeAliasDeclaration } from './typeAliasDeclaration';
42import type { TypeAliasEntity } from './typeAliasDeclaration';
43import { getVariableStatementDeclaration } from './variableStatementResolve';
44import type { StatementEntity } from './variableStatementResolve';
45
46interface SubstepGetModuleparam {
47  typeAliasDeclarations: Array<TypeAliasEntity>;
48  classDeclarations: Array<ClassEntity>;
49  interfaceDeclarations: Array<InterfaceEntity>;
50  functionDeclarations: Map<string, Array<FunctionEntity>>;
51  enumDeclarations: Array<EnumEntity>;
52  moduleDeclarations: Array<ModuleBlockEntity>;
53  variableStatements: Array<Array<StatementEntity>>;
54  moduleImportEquaqls: Array<ImportEuqalEntity>;
55  exportDeclarations: Array<string>;
56  sourceFile: SourceFile;
57  fileName: string;
58  moduleBody: ModuleBody;
59}
60
61export interface ModuleBlockEntity {
62  moduleName: string;
63  exportModifiers: Array<number>;
64  typeAliasDeclarations: Array<TypeAliasEntity>;
65  classDeclarations: Array<ClassEntity>;
66  interfaceDeclarations: Array<InterfaceEntity>;
67  functionDeclarations: Map<string, Array<FunctionEntity>>;
68  enumDeclarations: Array<EnumEntity>;
69  moduleDeclarations: Array<ModuleBlockEntity>;
70  variableStatements: Array<Array<StatementEntity>>;
71  moduleImportEquaqls: Array<ImportEuqalEntity>;
72  exportDeclarations: Array<string>;
73}
74
75/**
76 * get module info
77 * @param node
78 * @param sourceFile
79 * @param fileName
80 * @returns
81 */
82export function getModuleDeclaration(node: Node, sourceFile: SourceFile, fileName: string): ModuleBlockEntity {
83  const moduleNode = node as ModuleDeclaration;
84  const typeAliasDeclarations: Array<TypeAliasEntity> = [];
85  const classDeclarations: Array<ClassEntity> = [];
86  const interfaceDeclarations: Array<InterfaceEntity> = [];
87  const functionDeclarations: Map<string, Array<FunctionEntity>> = new Map<string, Array<FunctionEntity>>();
88  const enumDeclarations: Array<EnumEntity> = [];
89  const moduleDeclarations: Array<ModuleBlockEntity> = [];
90  const variableStatements: Array<Array<StatementEntity>> = [];
91  const moduleImportEquaqls: Array<ImportEuqalEntity> = [];
92  const exportDeclarations: Array<string> = [];
93  const moduleBody = moduleNode.body;
94  let moduleName = '';
95  if (isIdentifier(moduleNode.name)) {
96    moduleName = moduleNode.name.escapedText.toString();
97  } else {
98    moduleName = sourceFile.text.substring(moduleNode.name.pos, moduleNode.name.end).trim();
99  }
100
101  let exportModifiers: Array<number> = [];
102  const modifiers = moduleNode.modifiers;
103  if (modifiers !== undefined) {
104    exportModifiers = getExportKeyword(modifiers);
105  }
106  const SubstepModuleBlockEntitys: SubstepGetModuleparam = substepModule({
107    typeAliasDeclarations,
108    classDeclarations,
109    interfaceDeclarations,
110    functionDeclarations,
111    enumDeclarations,
112    moduleDeclarations,
113    variableStatements,
114    moduleImportEquaqls,
115    exportDeclarations,
116    moduleBody,
117    sourceFile,
118    fileName
119  });
120  delete SubstepModuleBlockEntitys.moduleBody;
121  delete SubstepModuleBlockEntitys.sourceFile;
122  delete SubstepModuleBlockEntitys.fileName;
123  return {
124    ...SubstepModuleBlockEntitys,
125    exportModifiers,
126    moduleName
127  };
128}
129
130/**
131 * get some module info
132 * @param SubstepGetModuleparam
133 * @returns
134 */
135function substepModule(props: SubstepGetModuleparam): SubstepGetModuleparam {
136  if (props.moduleBody !== undefined && isModuleBlock(props.moduleBody)) {
137    props.moduleBody.statements.forEach(value => {
138      if (isFunctionDeclaration(value)) {
139        const functionEntity = getFunctionDeclaration(value, props.sourceFile);
140        if (props.functionDeclarations.get(functionEntity.functionName) !== undefined) {
141          props.functionDeclarations.get(functionEntity.functionName)?.push(functionEntity);
142        } else {
143          const functionArray: Array<FunctionEntity> = [];
144          functionArray.push(functionEntity);
145          props.functionDeclarations.set(functionEntity.functionName, functionArray);
146        }
147      } else if (isTypeAliasDeclaration(value)) {
148        props.typeAliasDeclarations.push(getTypeAliasDeclaration(value, props.sourceFile));
149      } else if (isEnumDeclaration(value)) {
150        props.enumDeclarations.push(getEnumDeclaration(value, props.sourceFile));
151      } else if (isClassDeclaration(value)) {
152        props.classDeclarations.push(getClassDeclaration(value, props.sourceFile));
153      } else if (isInterfaceDeclaration(value)) {
154        props.interfaceDeclarations.push(getInterfaceDeclaration(value, props.sourceFile));
155      } else if (isModuleDeclaration(value)) {
156        props.moduleDeclarations.push(getModuleDeclaration(value, props.sourceFile, props.fileName));
157      } else if (isVariableStatement(value)) {
158        props.variableStatements.push(getVariableStatementDeclaration(value, props.sourceFile));
159      } else if (isImportEqualsDeclaration(value)) {
160        props.moduleImportEquaqls.push(getModuleImportEqual(value, props.sourceFile));
161      } else if (isExportDeclaration(value)) {
162        props.exportDeclarations.push(getExportDeclaration(value, props.sourceFile));
163      } else {
164        console.log('--------------------------- uncaught module type start -----------------------');
165        console.log('fileName: ' + props.fileName);
166        console.log(value);
167        console.log('--------------------------- uncaught module type end -----------------------');
168      }
169    });
170  }
171  return props;
172}
173