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 path from 'path'; 17import { SyntaxKind } from 'typescript'; 18import type { SourceFile } from 'typescript'; 19import { firstCharacterToUppercase } from '../common/commonUtils'; 20import type { ModuleBlockEntity } from '../declaration-node/moduleDeclaration'; 21import { 22 getDefaultExportClassDeclaration, getSourceFileFunctions, 23 getSourceFileVariableStatements 24} from '../declaration-node/sourceFileElementsAssemply'; 25import { generateClassDeclaration } from './generateClassDeclaration'; 26import { generateCommonFunction } from './generateCommonFunction'; 27import { generateEnumDeclaration } from './generateEnumDeclaration'; 28import { generateImportEqual } from './generateImportEqual'; 29import { addToIndexArray } from './generateIndex'; 30import { generateInterfaceDeclaration } from './generateInterfaceDeclaration'; 31import { generateStaticFunction } from './generateStaticFunction'; 32import { addToSystemIndexArray } from './generateSystemIndex'; 33import { generateTypeAliasDeclaration } from './generateTypeAlias'; 34import { generateVariableStatementDelcatation } from './generateVariableStatementDeclaration'; 35 36/** 37 * generate declare 38 * @param rootName 39 * @param moduleEntity 40 * @param sourceFile 41 * @param filename 42 * @param extraImport 43 * @returns 44 */ 45export function generateModuleDeclaration(rootName: string, moduleEntity: ModuleBlockEntity, sourceFile: SourceFile, 46 filename: string, mockApi: string, extraImport: string[]): string { 47 let moduleName = moduleEntity.moduleName.replace(/["']/g, ''); 48 let moduleBody = `export function mock${firstCharacterToUppercase(moduleName)}() {\n`; 49 if (!(moduleEntity.exportModifiers.includes(SyntaxKind.DeclareKeyword) && 50 (moduleEntity.moduleName.startsWith('"') || moduleEntity.moduleName.startsWith('\''))) && 51 path.basename(sourceFile.fileName).startsWith('@ohos') 52 ) { 53 addToIndexArray({ fileName: filename, mockFunctionName: `mock${firstCharacterToUppercase(moduleName)}` }); 54 } 55 let outBody = ''; 56 const defaultExportClass = getDefaultExportClassDeclaration(sourceFile); 57 58 if (defaultExportClass.length > 0) { 59 defaultExportClass.forEach(value => { 60 if (value.exportModifiers.includes(SyntaxKind.DefaultKeyword) && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) { 61 if (filename.startsWith('system_')) { 62 const mockNameArr = filename.split('_'); 63 const mockName = mockNameArr[mockNameArr.length - 1]; 64 addToSystemIndexArray({ 65 filename: filename, 66 mockFunctionName: `mock${firstCharacterToUppercase(mockName)}` 67 }); 68 69 moduleBody += `global.systemplugin.${mockName} = {`; 70 if (value.staticMethods.length > 0) { 71 let staticMethodBody = ''; 72 value.staticMethods.forEach(val => { 73 staticMethodBody += generateStaticFunction(val, true, sourceFile, mockApi) + '\n'; 74 }); 75 moduleBody += staticMethodBody; 76 } 77 moduleBody += '}'; 78 } else { 79 outBody += generateClassDeclaration('', value, false, '', filename, sourceFile, false, mockApi); 80 } 81 } 82 }); 83 } 84 85 if (moduleEntity.typeAliasDeclarations.length > 0) { 86 moduleEntity.typeAliasDeclarations.forEach(value => { 87 outBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport) + '\n'; 88 }); 89 } 90 91 if (moduleEntity.moduleImportEquaqls.length > 0) { 92 moduleEntity.moduleImportEquaqls.forEach(value => { 93 outBody += generateImportEqual(value) + '\n'; 94 }); 95 } 96 97 if (moduleEntity.classDeclarations.length > 0) { 98 moduleEntity.classDeclarations.forEach(value => { 99 if (value.exportModifiers.length > 0 && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) { 100 outBody += generateClassDeclaration(moduleName, value, false, '', '', sourceFile, false, mockApi) + '\n'; 101 } else { 102 moduleBody += '\t' + generateClassDeclaration(moduleName, value, false, '', '', sourceFile, true, mockApi) + '\n'; 103 } 104 }); 105 } 106 107 if (moduleEntity.interfaceDeclarations.length > 0) { 108 moduleEntity.interfaceDeclarations.forEach(value => { 109 if (value.exportModifiers.length > 0) { 110 outBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations) + ';\n'; 111 } else { 112 moduleBody += '\t' + generateInterfaceDeclaration(moduleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations) + ';\n'; 113 } 114 }); 115 } 116 117 if (moduleEntity.enumDeclarations.length > 0) { 118 moduleEntity.enumDeclarations.forEach(value => { 119 if (value.exportModifiers.length > 0) { 120 outBody += generateEnumDeclaration(moduleName, value) + '\n'; 121 } else { 122 moduleBody += '\t' + generateEnumDeclaration(moduleName, value) + '\n'; 123 } 124 }); 125 } 126 127 let functionBody = ''; 128 if (moduleEntity.functionDeclarations.size > 0) { 129 moduleEntity.functionDeclarations.forEach(value => { 130 functionBody += '\t' + generateCommonFunction(moduleName, value, sourceFile, mockApi) + '\n'; 131 }); 132 } 133 134 moduleBody += '\t' + `const ${moduleName} = {`; 135 if (moduleEntity.variableStatements.length > 0) { 136 moduleEntity.variableStatements.forEach(value => { 137 value.forEach(val => { 138 moduleBody += generateVariableStatementDelcatation(val, false) + '\n'; 139 }); 140 }); 141 } 142 143 const sourceFileFunctions = getSourceFileFunctions(sourceFile); 144 let sourceFileFunctionBody = ''; 145 if (sourceFileFunctions.size > 0) { 146 sourceFileFunctions.forEach(value => { 147 sourceFileFunctionBody += generateCommonFunction(moduleName, value, sourceFile, mockApi); 148 }); 149 } 150 151 const sourceFileVariableStatements = getSourceFileVariableStatements(sourceFile); 152 let sourceFileStatementBody = ''; 153 if (sourceFileVariableStatements.length > 0) { 154 sourceFileVariableStatements.forEach(value => { 155 value.forEach(val => { 156 sourceFileStatementBody += generateVariableStatementDelcatation(val, false); 157 }); 158 }); 159 } 160 161 moduleBody += sourceFileFunctionBody + '\n'; 162 moduleBody += sourceFileStatementBody + '\n'; 163 moduleBody += functionBody + '\n'; 164 165 const exports = getModuleExportElements(moduleEntity); 166 let exportString = ''; 167 exports.forEach(value => { 168 exportString += `${value.name}: ${value.name},\n`; 169 }); 170 if (exportString !== '') { 171 moduleBody += '\t' + exportString; 172 } 173 174 moduleBody += '\t};'; 175 moduleBody += `\n\treturn ${moduleName};}\n`; 176 moduleBody += outBody; 177 return moduleBody; 178} 179 180/** 181 * generate inner module for declare module 182 * @param moduleEntity 183 * @returns 184 */ 185function generateInnerDeclareModule(moduleEntity: ModuleBlockEntity): string { 186 let moduleName = '$' + moduleEntity.moduleName.replace(/["']/g, ''); 187 let module = `\n\texport const ${moduleName} = `; 188 if (moduleEntity.exportDeclarations.length > 0) { 189 moduleEntity.exportDeclarations.forEach(value => { 190 module += value.match(/{[^{}]*}/g)[0] + '\n'; 191 }); 192 } 193 return module; 194} 195 196/** 197 * generate inner module 198 * @param moduleEntity 199 * @param sourceFile 200 * @param extraImport 201 * @returns 202 */ 203function generateInnerModule(moduleEntity: ModuleBlockEntity, sourceFile: SourceFile, extraImport: string[]): string { 204 const moduleName = moduleEntity.moduleName; 205 let innerModuleBody = `const ${moduleName} = (()=> {`; 206 207 if (moduleEntity.enumDeclarations.length > 0) { 208 moduleEntity.enumDeclarations.forEach(value => { 209 innerModuleBody += generateEnumDeclaration(moduleName, value) + '\n'; 210 }); 211 } 212 213 if (moduleEntity.typeAliasDeclarations.length > 0) { 214 moduleEntity.typeAliasDeclarations.forEach(value => { 215 innerModuleBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport) + '\n'; 216 }); 217 } 218 219 if (moduleEntity.moduleImportEquaqls.length > 0) { 220 moduleEntity.moduleImportEquaqls.forEach(value => { 221 innerModuleBody += generateImportEqual(value) + '\n'; 222 }); 223 } 224 225 if (moduleEntity.interfaceDeclarations.length > 0) { 226 moduleEntity.interfaceDeclarations.forEach(value => { 227 innerModuleBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, '', moduleEntity.interfaceDeclarations) + '\n'; 228 }); 229 } 230 231 let functionBody = 'return {'; 232 if (moduleEntity.functionDeclarations.size > 0) { 233 moduleEntity.functionDeclarations.forEach(value => { 234 functionBody += generateCommonFunction(moduleName, value, sourceFile, '') + '\n'; 235 }); 236 } 237 238 if (moduleEntity.variableStatements.length > 0) { 239 moduleEntity.variableStatements.forEach(value => { 240 value.forEach(val => { 241 innerModuleBody += generateVariableStatementDelcatation(val, true) + '\n'; 242 }); 243 }); 244 } 245 innerModuleBody += functionBody + '\n'; 246 247 const exports = getModuleExportElements(moduleEntity); 248 let exportString = ''; 249 exports.forEach(value => { 250 exportString += `${value.name}: ${value.name},\n`; 251 }); 252 if (exportString !== '') { 253 innerModuleBody += '\t' + exportString; 254 } 255 innerModuleBody += '\t};})();'; 256 return innerModuleBody; 257} 258 259/** 260 * get all export elements 261 * @param moduleEntity 262 * @returns 263 */ 264function getModuleExportElements(moduleEntity: ModuleBlockEntity): Array<ModuleExportEntity> { 265 const exportElements: Array<ModuleExportEntity> = []; 266 if (moduleEntity.moduleName.startsWith('"') && moduleEntity.moduleName.endsWith('"')) { 267 return exportElements; 268 } 269 if (moduleEntity.classDeclarations.length > 0) { 270 moduleEntity.classDeclarations.forEach(value => { 271 exportElements.push({ name: firstCharacterToUppercase(value.className), type: 'class' }); 272 }); 273 } 274 275 if (moduleEntity.interfaceDeclarations.length > 0) { 276 moduleEntity.interfaceDeclarations.forEach(value => { 277 exportElements.push({ name: value.interfaceName, type: 'interface' }); 278 }); 279 } 280 281 if (moduleEntity.enumDeclarations.length > 0) { 282 moduleEntity.enumDeclarations.forEach(value => { 283 exportElements.push({ name: value.enumName, type: 'enum' }); 284 }); 285 } 286 287 if (moduleEntity.moduleDeclarations.length > 0) { 288 moduleEntity.moduleDeclarations.forEach(value => { 289 exportElements.push({ name: value.moduleName, type: 'module' }); 290 }); 291 } 292 293 if (moduleEntity.typeAliasDeclarations.length > 0) { 294 moduleEntity.typeAliasDeclarations.forEach(value => { 295 exportElements.push({ name: value.typeAliasName, type: 'type' }); 296 }); 297 } 298 return exportElements; 299} 300 301interface ModuleExportEntity { 302 type: string, 303 name: string 304} 305