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