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'; 35import type { ImportElementEntity } from '../declaration-node/importAndExportDeclaration'; 36 37/** 38 * generate declare 39 * @param rootName 40 * @param moduleEntity 41 * @param sourceFile 42 * @param filename 43 * @param extraImport 44 * @returns 45 */ 46export function generateModuleDeclaration(rootName: string, moduleEntity: ModuleBlockEntity, sourceFile: SourceFile, 47 filename: string, mockApi: string, extraImport: string[], importDeclarations: ImportElementEntity[]): string { 48 let innerModuleBody = ''; 49 const moduleName = moduleEntity.moduleName.replace(/["']/g, ''); 50 let moduleBody = `export function mock${firstCharacterToUppercase(moduleName)}() {\n`; 51 let enumBody = ''; 52 if (!(moduleEntity.exportModifiers.includes(SyntaxKind.DeclareKeyword) && 53 (moduleEntity.moduleName.startsWith('"') || moduleEntity.moduleName.startsWith('\''))) && 54 path.basename(sourceFile.fileName).startsWith('@ohos') 55 ) { 56 addToIndexArray({ fileName: filename, mockFunctionName: `mock${firstCharacterToUppercase(moduleName)}` }); 57 } 58 let outBody = ''; 59 const defaultExportClass = getDefaultExportClassDeclaration(sourceFile); 60 61 if (defaultExportClass.length > 0) { 62 defaultExportClass.forEach(value => { 63 if (value.exportModifiers.includes(SyntaxKind.DefaultKeyword) && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) { 64 if (filename.startsWith('system_')) { 65 const mockNameArr = filename.split('_'); 66 const mockName = mockNameArr[mockNameArr.length - 1]; 67 addToSystemIndexArray({ 68 filename: filename, 69 mockFunctionName: `mock${firstCharacterToUppercase(mockName)}` 70 }); 71 72 moduleBody += `global.systemplugin.${mockName} = {`; 73 if (value.staticMethods.length > 0) { 74 let staticMethodBody = ''; 75 value.staticMethods.forEach(val => { 76 staticMethodBody += generateStaticFunction(val, true, sourceFile, mockApi) + '\n'; 77 }); 78 moduleBody += staticMethodBody; 79 } 80 moduleBody += '}'; 81 } else { 82 outBody += generateClassDeclaration('', value, false, '', filename, sourceFile, false, mockApi); 83 } 84 } 85 }); 86 } 87 88 if (moduleEntity.typeAliasDeclarations.length > 0) { 89 moduleEntity.typeAliasDeclarations.forEach(value => { 90 outBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport, mockApi) + '\n'; 91 }); 92 } 93 94 if (moduleEntity.moduleImportEquaqls.length > 0) { 95 moduleEntity.moduleImportEquaqls.forEach(value => { 96 outBody += generateImportEqual(value) + '\n'; 97 }); 98 } 99 100 if (moduleEntity.classDeclarations.length > 0) { 101 moduleEntity.classDeclarations.forEach(value => { 102 if (value.exportModifiers.length > 0 && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) { 103 outBody += generateClassDeclaration(moduleName, value, false, '', '', sourceFile, false, mockApi) + '\n'; 104 } else { 105 moduleBody += '\t' + generateClassDeclaration(moduleName, value, false, '', '', sourceFile, true, mockApi) + '\n'; 106 } 107 }); 108 } 109 110 if (moduleEntity.interfaceDeclarations.length > 0) { 111 moduleEntity.interfaceDeclarations.forEach(value => { 112 if (value.exportModifiers.length > 0) { 113 outBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations, importDeclarations, extraImport) + ';\n'; 114 } else { 115 moduleBody += '\t' + generateInterfaceDeclaration(moduleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations, importDeclarations, extraImport) + ';\n'; 116 } 117 }); 118 } 119 120 if (moduleEntity.enumDeclarations.length > 0) { 121 moduleEntity.enumDeclarations.forEach(value => { 122 if (value.exportModifiers.length > 0) { 123 outBody += generateEnumDeclaration(moduleName, value) + '\n'; 124 } else { 125 enumBody += generateEnumDeclaration(moduleName, value); 126 } 127 }); 128 } 129 130 let functionBody = ''; 131 if (moduleEntity.functionDeclarations.size > 0) { 132 moduleEntity.functionDeclarations.forEach(value => { 133 functionBody += '\t' + generateCommonFunction(moduleName, value, sourceFile, mockApi, false) + '\n'; 134 }); 135 } 136 137 if (moduleEntity.moduleDeclarations.length > 0) { 138 moduleEntity.moduleDeclarations.forEach(value => { 139 if (!value.moduleName.startsWith("'") && !value.moduleName.startsWith('"')) { 140 innerModuleBody += generateInnerModuleDeclaration(value, sourceFile, filename, mockApi, extraImport, importDeclarations); 141 } 142 }); 143 } 144 if (innerModuleBody) { 145 moduleBody += innerModuleBody + '\n'; 146 } 147 148 moduleBody += '\t' + `const ${moduleName} = {`; 149 if (moduleEntity.variableStatements.length > 0) { 150 moduleEntity.variableStatements.forEach(value => { 151 value.forEach(val => { 152 moduleBody += generateVariableStatementDelcatation(val, false) + '\n'; 153 }); 154 }); 155 } 156 157 const sourceFileFunctions = getSourceFileFunctions(sourceFile); 158 let sourceFileFunctionBody = ''; 159 if (sourceFileFunctions.size > 0) { 160 sourceFileFunctions.forEach(value => { 161 sourceFileFunctionBody += '\n' + generateCommonFunction(moduleName, value, sourceFile, mockApi, false); 162 }); 163 } 164 165 const sourceFileVariableStatements = getSourceFileVariableStatements(sourceFile); 166 let sourceFileStatementBody = ''; 167 if (sourceFileVariableStatements.length > 0) { 168 sourceFileVariableStatements.forEach(value => { 169 value.forEach(val => { 170 sourceFileStatementBody += '\n' + generateVariableStatementDelcatation(val, false); 171 }); 172 }); 173 } 174 175 moduleBody += sourceFileFunctionBody + '\n'; 176 moduleBody += sourceFileStatementBody + '\n'; 177 moduleBody += functionBody + '\n'; 178 179 const exports = getModuleExportElements(moduleEntity); 180 let exportString = ''; 181 exports.forEach(value => { 182 if (value.type === 'module' && !value.name.startsWith("'") && !value.name.startsWith('"')) { 183 exportString += `${value.name}: mock${value.name}(),\n`; 184 } else { 185 exportString += `${value.name}: ${value.name},\n`; 186 } 187 }); 188 if (exportString !== '') { 189 moduleBody += '\t' + exportString; 190 } 191 192 moduleBody += '\t};'; 193 moduleBody += `\n\treturn ${moduleName};}\n`; 194 moduleBody += outBody; 195 moduleBody = enumBody + moduleBody; 196 return moduleBody; 197} 198 199function generateInnerModuleDeclaration(moduleEntity: ModuleBlockEntity, sourceFile: SourceFile, 200 filename: string, mockApi: string, extraImport: string[], importDeclarations: ImportElementEntity[]): string { 201 let innerModuleBody = ''; 202 const innerModuleName = moduleEntity.moduleName.replace(/["']/g, ''); 203 let moduleBody = `function mock${innerModuleName}() {\n`; 204 let innerOutBody = ''; 205 let innerFunctionBody = ''; 206 207 if (moduleEntity.typeAliasDeclarations.length) { 208 moduleEntity.typeAliasDeclarations.forEach(value => { 209 innerOutBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport, mockApi) + '\n'; 210 }); 211 } 212 213 if (moduleEntity.moduleImportEquaqls.length) { 214 moduleEntity.moduleImportEquaqls.forEach(value => { 215 innerOutBody += generateImportEqual(value) + '\n'; 216 }); 217 } 218 219 if (moduleEntity.classDeclarations.length) { 220 moduleEntity.classDeclarations.forEach(value => { 221 if (value.exportModifiers.length && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) { 222 innerOutBody += generateClassDeclaration(innerModuleName, value, false, '', '', sourceFile, false, mockApi) + '\n'; 223 } else { 224 moduleBody += '\t' + generateClassDeclaration(innerModuleName, value, false, '', '', sourceFile, true, mockApi) + '\n'; 225 } 226 }); 227 } 228 229 if (moduleEntity.interfaceDeclarations.length) { 230 moduleEntity.interfaceDeclarations.forEach(value => { 231 if (value.exportModifiers.length) { 232 innerOutBody += generateInterfaceDeclaration(innerModuleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations, importDeclarations, extraImport) + ';\n'; 233 } else { 234 moduleBody += '\t' + generateInterfaceDeclaration(innerModuleName, value, sourceFile, false, mockApi, moduleEntity.interfaceDeclarations, importDeclarations, extraImport) + ';\n'; 235 } 236 }); 237 } 238 239 if (moduleEntity.enumDeclarations.length) { 240 moduleEntity.enumDeclarations.forEach(value => { 241 if (value.exportModifiers.length) { 242 innerOutBody += generateEnumDeclaration(innerModuleName, value) + '\n'; 243 } else { 244 moduleBody += generateEnumDeclaration(innerModuleName, value); 245 } 246 }); 247 } 248 249 if (moduleEntity.functionDeclarations.size) { 250 moduleEntity.functionDeclarations.forEach(value => { 251 innerFunctionBody += '\n' + generateCommonFunction(innerModuleName, value, sourceFile, mockApi, false) + '\n'; 252 }); 253 } 254 255 if (moduleEntity.moduleDeclarations.length) { 256 moduleEntity.moduleDeclarations.forEach(value => { 257 if (!value.moduleName.startsWith("'") && !value.moduleName.startsWith('"')) { 258 innerModuleBody += generateInnerModuleDeclaration(value, sourceFile, filename, mockApi, extraImport, importDeclarations); 259 } 260 }); 261 } 262 if (innerModuleBody) { 263 moduleBody += innerModuleBody + '\n'; 264 } 265 266 moduleBody += `const ${innerModuleName} = {\n`; 267 if (moduleEntity.variableStatements.length) { 268 moduleEntity.variableStatements.forEach(value => { 269 value.forEach(val => { 270 moduleBody += generateVariableStatementDelcatation(val, false) + '\n'; 271 }); 272 }); 273 } 274 275 moduleBody += innerFunctionBody + '\n'; 276 277 const exportArr = getModuleExportElements(moduleEntity); 278 let innerExportString = ''; 279 exportArr.forEach(value => { 280 if (value.type === 'module' && !value.name.startsWith("'") && !value.name.startsWith('"')) { 281 innerExportString += `${value.name}: mock${value.name}(),\n`; 282 } else { 283 innerExportString += `${value.name}: ${value.name},\n`; 284 } 285 }); 286 if (innerExportString !== '') { 287 moduleBody += '\t' + innerExportString; 288 } 289 290 moduleBody += '\t};'; 291 moduleBody += `\n\treturn ${innerModuleName};}\n`; 292 moduleBody += innerOutBody; 293 return moduleBody; 294} 295 296/** 297 * generate inner module for declare module 298 * @param moduleEntity 299 * @returns 300 */ 301function generateInnerDeclareModule(moduleEntity: ModuleBlockEntity): string { 302 let moduleName = '$' + moduleEntity.moduleName.replace(/["']/g, ''); 303 let module = `\n\texport const ${moduleName} = `; 304 if (moduleEntity.exportDeclarations.length > 0) { 305 moduleEntity.exportDeclarations.forEach(value => { 306 module += value.match(/{[^{}]*}/g)[0] + '\n'; 307 }); 308 } 309 return module; 310} 311 312/** 313 * generate inner module 314 * @param moduleEntity 315 * @param sourceFile 316 * @param extraImport 317 * @returns 318 */ 319function generateInnerModule(moduleEntity: ModuleBlockEntity, sourceFile: SourceFile, extraImport: string[], mockApi: string): string { 320 const moduleName = moduleEntity.moduleName; 321 let innerModuleBody = `const ${moduleName} = (()=> {`; 322 323 if (moduleEntity.enumDeclarations.length > 0) { 324 moduleEntity.enumDeclarations.forEach(value => { 325 innerModuleBody += generateEnumDeclaration(moduleName, value) + '\n'; 326 }); 327 } 328 329 if (moduleEntity.typeAliasDeclarations.length > 0) { 330 moduleEntity.typeAliasDeclarations.forEach(value => { 331 innerModuleBody += generateTypeAliasDeclaration(value, true, sourceFile, extraImport, mockApi) + '\n'; 332 }); 333 } 334 335 if (moduleEntity.moduleImportEquaqls.length > 0) { 336 moduleEntity.moduleImportEquaqls.forEach(value => { 337 innerModuleBody += generateImportEqual(value) + '\n'; 338 }); 339 } 340 341 if (moduleEntity.interfaceDeclarations.length > 0) { 342 moduleEntity.interfaceDeclarations.forEach(value => { 343 innerModuleBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, '', moduleEntity.interfaceDeclarations) + '\n'; 344 }); 345 } 346 347 let functionBody = 'return {'; 348 if (moduleEntity.functionDeclarations.size > 0) { 349 moduleEntity.functionDeclarations.forEach(value => { 350 functionBody += generateCommonFunction(moduleName, value, sourceFile, '', false) + '\n'; 351 }); 352 } 353 354 if (moduleEntity.variableStatements.length > 0) { 355 moduleEntity.variableStatements.forEach(value => { 356 value.forEach(val => { 357 innerModuleBody += generateVariableStatementDelcatation(val, true) + '\n'; 358 }); 359 }); 360 } 361 innerModuleBody += functionBody + '\n'; 362 363 const exports = getModuleExportElements(moduleEntity); 364 let exportString = ''; 365 exports.forEach(value => { 366 exportString += `${value.name}: ${value.name},\n`; 367 }); 368 if (exportString !== '') { 369 innerModuleBody += '\t' + exportString; 370 } 371 innerModuleBody += '\t};})();'; 372 return innerModuleBody; 373} 374 375/** 376 * get all export elements 377 * @param moduleEntity 378 * @returns 379 */ 380function getModuleExportElements(moduleEntity: ModuleBlockEntity): Array<ModuleExportEntity> { 381 const exportElements: Array<ModuleExportEntity> = []; 382 if (moduleEntity.moduleName.startsWith('"') && moduleEntity.moduleName.endsWith('"')) { 383 return exportElements; 384 } 385 if (moduleEntity.classDeclarations.length > 0) { 386 moduleEntity.classDeclarations.forEach(value => { 387 exportElements.push({ name: firstCharacterToUppercase(value.className), type: 'class' }); 388 }); 389 } 390 391 if (moduleEntity.interfaceDeclarations.length > 0) { 392 moduleEntity.interfaceDeclarations.forEach(value => { 393 exportElements.push({ name: value.interfaceName, type: 'interface' }); 394 }); 395 } 396 397 if (moduleEntity.enumDeclarations.length > 0) { 398 moduleEntity.enumDeclarations.forEach(value => { 399 exportElements.push({ name: value.enumName, type: 'enum' }); 400 }); 401 } 402 403 if (moduleEntity.moduleDeclarations.length > 0) { 404 moduleEntity.moduleDeclarations.forEach(value => { 405 exportElements.push({ name: value.moduleName, type: 'module' }); 406 }); 407 } 408 409 if (moduleEntity.typeAliasDeclarations.length > 0) { 410 moduleEntity.typeAliasDeclarations.forEach(value => { 411 exportElements.push({ name: value.typeAliasName, type: 'type' }); 412 }); 413 } 414 return exportElements; 415} 416 417interface ModuleExportEntity { 418 type: string, 419 name: string 420} 421