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 fs from 'fs'; 18import { Node, SourceFile, JSDoc } from 'typescript'; 19import { KeyValue, Members, MockBuffer } from '../types'; 20import { D_ETS, D_TS, KeyValueTypes, NAPI_DIR_PATH, needShieldingDirOrFile } from './constants'; 21import { getProjectDir } from './systemUtils'; 22 23/** 24 * 递归创建文件夹 25 * @param dirname 需要创建文件夹的路径 26 * @returns 是否创建成功 27 */ 28export function mkdirsSync(dirname: string): boolean { 29 if (fs.existsSync(dirname)) { 30 return true; 31 } else { 32 if (mkdirsSync(path.dirname(dirname))) { 33 fs.mkdirSync(dirname); 34 return true; 35 } 36 } 37 return false; 38} 39 40/** 41 * 生成KeyValue节点 42 * @param key 节点名称 43 * @param type 节点类型 44 * @param parent 该节点的父节点 45 * @param optionMembers 其他初始化参数 46 * @returns KeyValue节点 47 */ 48export function generateKeyValue(key: string, type: KeyValueTypes, parent?: KeyValue, optionMembers?: { 49 members: Members, 50 typeParameters: Members, 51 methodParams: Members, 52 constraint: Members, 53 sameName: KeyValue[], 54 dependOnGlobals: Set<KeyValue> 55}): KeyValue { 56 const keyValue = {key, type, parent, sameDeclares: []} as KeyValue; 57 if (optionMembers) { 58 keyValue.members = optionMembers.members ?? {}; 59 keyValue.typeParameters = optionMembers.typeParameters ?? {}; 60 keyValue.methodParams = optionMembers.methodParams ?? {}; 61 keyValue.constraint = optionMembers.constraint ?? {}; 62 keyValue.sameName = optionMembers.sameName ?? []; 63 keyValue.dependOnGlobals = optionMembers.dependOnGlobals ?? new Set<KeyValue>(); 64 } else { 65 keyValue.members = {}; 66 keyValue.typeParameters = {}; 67 keyValue.methodParams = {}; 68 keyValue.constraint = {}; 69 keyValue.sameName = []; 70 keyValue.dependOnGlobals = new Set<KeyValue>(); 71 } 72 return keyValue as KeyValue; 73} 74 75/** 76 * 获取开源接口的api路径 77 */ 78export function getOhosInterfacesDir(): string { 79 return path.join(getProjectDir(), 'interface', 'sdk-js', 'api'); 80} 81 82/** 83 * 获取导入路径的绝对路径 84 * @param mockBuffer 文件mock缓存信息 85 * @param specifier 导入路径 86 * @returns 导入文件的绝对路径 87 */ 88export function getAbsolutePath(mockBuffer: MockBuffer, specifier: string): string { 89 let absolutePath: string; 90 const importPath = specifier.replace(/['"]/g, ''); 91 if (importPath.startsWith('./') || importPath.startsWith('../')) { 92 absolutePath = path.resolve(path.dirname(mockBuffer.mockedFilePath), importPath) + '.js'; 93 } else if (importPath.startsWith('@ohos.') || importPath.startsWith('@system.')) { 94 absolutePath = path.join(NAPI_DIR_PATH, 'api', importPath) + '.js'; 95 } else if (importPath.startsWith('@kit.')) { 96 absolutePath = path.join(NAPI_DIR_PATH, 'kits', importPath) + '.js'; 97 } else if (importPath.startsWith('@arkts.')) { 98 absolutePath = path.join(NAPI_DIR_PATH, 'arkts', importPath) + '.js'; 99 } else { 100 absolutePath = path.resolve(path.dirname(mockBuffer.mockedFilePath), importPath) + '.js'; 101 } 102 return absolutePath; 103} 104 105/** 106 * 处理迭代器函数 107 * @param typeParams 迭代器返回值的类型参数 108 */ 109export function handleIterableIterator(typeParams: string): string { 110 return `function* () { 111 const yieldObj = ${typeParams}; 112 yield yieldObj; 113 yield yieldObj; 114 yield yieldObj; 115 }`; 116} 117 118/** 119 * 关联类型参数 120 * @param keyValue KV节点 121 * @param typeParameters 122 */ 123export function associateTypeParameters(keyValue: KeyValue, typeParameters: Members): void { 124 if (!keyValue.property) { 125 keyValue.typeParameters = typeParameters; 126 return; 127 } 128 associateTypeParameters(keyValue.property, typeParameters); 129} 130 131/** 132 * 判断是否是类型申明文件 133 * @param fileName 文件名 134 */ 135export function isDeclarationFile(fileName: string): boolean { 136 return fileName.endsWith(D_TS) || fileName.endsWith(D_ETS); 137} 138 139/** 140 * 判断文件是否需要被mock 141 * @param filePath 文件路径 142 */ 143export function isNeedMocked(filePath: string): boolean { 144 if (path.basename(filePath).startsWith('@ohos.')) { 145 return true; 146 } 147 if (path.basename(filePath).startsWith('@arkts.')) { 148 return true; 149 } 150 return path.basename(filePath).startsWith('@kit.'); 151} 152 153/** 154 * .d.ets 和 .d.ts文件同时存在时,过滤掉.d.ets文件 155 * @param value 文件名称 156 * @returns boolean 157 */ 158export function identifyDuplicateFile(value: string, arr: string[]): boolean { 159 const baseName = path.basename(value); 160 if (baseName.includes('.static.d.')) { 161 return true; 162 } 163 const extName = path.extname(value); 164 if (extName === '.ets' && arr.includes(value.replace('.ets', '.ts'))) { 165 return true; 166 } 167 return false; 168} 169 170/** 171 * 过滤掉不需要mock的文件 172 * @param value 文件名称 173 * @returns boolean 174 */ 175export function filterDuplicateFile(value: string): boolean { 176 for (let i = 0; i < needShieldingDirOrFile.length; i++) { 177 if (value.includes(needShieldingDirOrFile[i])) { 178 return true; 179 } 180 } 181 return false; 182} 183 184/** 185 * Determine whether the content contains arkts1.2 by parsing the comments. 186 * @param node 187 * @param sourceFile 188 * @returns boolean 189 */ 190export function isArktsOne(node: Node, sourceFile: SourceFile): boolean { 191 const jsDocNode = node['jsDoc'] as JSDoc[]; 192 if (!jsDocNode) { 193 return true; 194 } 195 for (let i = 0; i < jsDocNode.length; i++) { 196 const element = jsDocNode[i]; 197 if (sourceFile.text.substring(element.pos, element.end).includes('@arkts 1.2')) { 198 return false; 199 } 200 } 201 return true; 202} 203