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