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 fs from 'fs'; 17import path from 'path'; 18import { createSourceFile, ScriptTarget } from 'typescript'; 19import { 20 getOhosInterfacesDir, 21 isDeclarationFile, 22 mkdirsSync, 23 generateKeyValue, 24 isNeedMocked 25} from './common/commonUtils'; 26import { getSourceFileAssembly } from './declaration-node/sourceFileElementsAssemply'; 27import { generateEntry } from './generate/generateEntry'; 28import { generateIndex } from './generate/generateIndex'; 29import { generateSystemIndex } from './generate/generateSystemIndex'; 30import { 31 arktsDtsFileList, 32 componentDtsFileList, 33 D_ETS, D_TS, 34 KeyValueTypes, 35 kitsDtsFileList, 36 mockBufferMap, 37 MockedFileMap, 38 NO_CONTENT_FILES, 39 ohosDtsFileList, 40 RawFileMap 41} from './common/constants'; 42import { ApiFolder, MockBuffer } from './types'; 43import { generateContent, handleDeclares } from './generate/generateContent'; 44 45/** 46 * 获取所有接口文件路径 47 * @param dir 接口目录路径 48 * @param fileList 接口文件列表 49 * @param isHmsDtsFile 是否是harmonyOS接口 50 * @returns 51 */ 52function getAllDtsFile(dir: string, fileList: string[], isHmsDtsFile: boolean): string[] { 53 const arr = fs.readdirSync(dir); 54 if (!dir.toString().includes('node_modules') && !dir.toString().includes(path.join('@internal', 'component'))) { 55 arr.forEach(value => { 56 collectFile(dir, fileList, value, isHmsDtsFile); 57 }); 58 } 59 return fileList; 60} 61 62function collectFile(dir: string, fileList: string[], value: string, isHmsDtsFile: boolean): void { 63 const fullPath = path.join(dir, value); 64 const stats = fs.statSync(fullPath); 65 if (stats.isDirectory()) { 66 getAllDtsFile(fullPath, fileList, isHmsDtsFile); 67 } else { 68 if (!isDeclarationFile(value)) { 69 return; 70 } 71 fileList.push(fullPath); 72 } 73} 74 75/** 76 * 获取所有组件接口文件路径 77 * @param dir 组件目录路径 78 * @returns 79 */ 80function getAllComponentsFilePath(dir: string): Array<string> { 81 const componentPath = path.join(dir, '@internal', 'component', 'ets'); 82 if (!fs.existsSync(componentPath)) { 83 return; 84 } 85 const componentPathArr = fs.readdirSync(componentPath); 86 componentPathArr.forEach(value => { 87 const fullPath = path.join(componentPath, value); 88 if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) { 89 if (!isDeclarationFile(value)) { 90 return; 91 } 92 componentDtsFileList.push(fullPath); 93 } 94 }); 95} 96 97/** 98 * 获取所有global接口文件路径 99 * @param dir global目录路径 100 */ 101function getAllGlobalFilePath(dir: string): Array<string> { 102 const globalPath = path.join(dir, 'global'); 103 if (!fs.existsSync(globalPath)) { 104 return; 105 } 106 const globalPathArr = fs.readdirSync(globalPath); 107 globalPathArr.forEach(value => { 108 const fullPath = path.join(globalPath, value); 109 if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) { 110 if (!isDeclarationFile(value)) { 111 return; 112 } 113 ohosDtsFileList.push(fullPath); 114 } 115 }); 116} 117 118/** 119 * 获取mock后的接口文件路径 120 * @param beforeMockedFilePath mock钱的文件路径 121 * @param apiFolder api目录路径 122 */ 123function getMockedFileName(beforeMockedFilePath: string, apiFolder: ApiFolder): string { 124 const outMockJsFolder = path.join(__dirname, '..', '..', 'runtime', 'main', 'extend', 'systemplugin'); 125 let fileName: string; 126 if (beforeMockedFilePath.endsWith(D_TS)) { 127 fileName = path.basename(beforeMockedFilePath, D_TS); 128 } else if (beforeMockedFilePath.endsWith(D_ETS)) { 129 fileName = path.basename(beforeMockedFilePath, D_ETS); 130 } else { 131 return ''; 132 } 133 134 let tmpOutputMockJsFileDir = outMockJsFolder; 135 if (!fileName.startsWith('@system.')) { 136 tmpOutputMockJsFileDir = path.join(outMockJsFolder, 'napi', apiFolder); 137 } 138 let dirName: string; 139 switch (apiFolder) { 140 case 'api': { 141 dirName = path.join(tmpOutputMockJsFileDir, path.dirname(beforeMockedFilePath).split(`${path.sep}api`)[1]); 142 break; 143 } 144 case 'component': { 145 dirName = tmpOutputMockJsFileDir; 146 break; 147 } 148 case 'arkts': { 149 dirName = path.join(tmpOutputMockJsFileDir, path.dirname(beforeMockedFilePath).split(`${path.sep}arkts`)[1]); 150 break; 151 } 152 case 'kits': { 153 dirName = path.join(tmpOutputMockJsFileDir, path.dirname(beforeMockedFilePath).split(`${path.sep}kits`)[1]); 154 break; 155 } 156 } 157 158 if (!fs.existsSync(dirName)) { 159 mkdirsSync(dirName); 160 } 161 return path.join(dirName, fileName + '.js'); 162} 163 164/** 165 * 初始化接口文件mock信息 166 * @param typeFile 类型文件路径 167 * @param apiFolder api目录路径 168 */ 169function initialTypeFile(typeFile: string, apiFolder: ApiFolder): void { 170 if (!isDeclarationFile(typeFile)) { 171 return; 172 } 173 const filePath = getMockedFileName(typeFile, apiFolder); 174 if (mockBufferMap.get(typeFile)) { 175 return; 176 } 177 const mockBuffer: MockBuffer = { 178 rawFilePath: typeFile, 179 mockedFilePath: filePath, 180 contents: generateKeyValue(filePath, KeyValueTypes.FILE) 181 }; 182 mockBufferMap.set(typeFile, mockBuffer); 183 RawFileMap.set(typeFile, filePath); 184 MockedFileMap.set(filePath, typeFile); 185} 186 187/** 188 * 处理所有ets文件的mock逻辑 189 */ 190function etsFileToMock(): void { 191 componentDtsFileList.forEach(file => initialTypeFile(file, 'component')); 192 ohosDtsFileList.forEach(file => initialTypeFile(file, 'api')); 193 arktsDtsFileList.forEach(file => initialTypeFile(file, 'arkts')); 194 kitsDtsFileList.forEach(file => initialTypeFile(file, 'kits')); 195 196 const allFileList = [ 197 ...componentDtsFileList, 198 ...ohosDtsFileList, 199 ...arktsDtsFileList, 200 ...kitsDtsFileList 201 ]; 202 203 allFileList.forEach(file => { 204 const code = fs.readFileSync(file); 205 const sourceFile = createSourceFile(file, code.toString().replace(/ struct /g, ' class '), ScriptTarget.Latest); 206 const mockBuffer = mockBufferMap.get(file); 207 getSourceFileAssembly(sourceFile, mockBuffer, mockBuffer.contents.members, mockBuffer.contents); 208 }); 209 210 handleDeclares(path.join(__dirname, '..', '..', 'runtime', 'main', 'extend', 'systemplugin')); 211 212 [ 213 ...ohosDtsFileList, 214 ...arktsDtsFileList 215 ].forEach(filepath => { 216 if (!isDeclarationFile(filepath)) { 217 return; 218 } 219 if (!isNeedMocked(filepath)) { 220 return; 221 } 222 const mockBuffer = mockBufferMap.get(filepath); 223 const keyValue = mockBuffer.contents; 224 const mockedContents = generateContent(mockBuffer, keyValue.members); 225 if (!mockedContents.length) { 226 NO_CONTENT_FILES.add(filepath); 227 } else { 228 fs.writeFileSync(mockBuffer.mockedFilePath, mockedContents); 229 } 230 }); 231} 232 233/** 234 * 项目入口函数 235 */ 236function main(): void { 237 const outMockJsFileDir = path.join(__dirname, '..', '..', 'runtime', 'main', 'extend', 'systemplugin'); 238 getAllGlobalFilePath(getOhosInterfacesDir()); 239 getAllComponentsFilePath(getOhosInterfacesDir()); 240 getAllDtsFile(getOhosInterfacesDir(), ohosDtsFileList, false); 241 getAllDtsFile(path.resolve(getOhosInterfacesDir(), '..', 'arkts'), arktsDtsFileList, false); 242 getAllDtsFile(path.resolve(getOhosInterfacesDir(), '..', 'kits'), kitsDtsFileList, false); 243 244 etsFileToMock(); 245 246 if (!fs.existsSync(path.join(outMockJsFileDir, 'napi'))) { 247 mkdirsSync(path.join(outMockJsFileDir, 'napi')); 248 } 249 const napiIndexPath = path.join(outMockJsFileDir, 'napi', 'index.js'); 250 fs.writeFileSync(napiIndexPath, generateIndex(napiIndexPath)); 251 fs.writeFileSync(path.join(outMockJsFileDir, 'index.js'), generateSystemIndex()); 252 fs.writeFileSync(path.join(outMockJsFileDir, 'entry.js'), generateEntry()); 253} 254 255main(); 256