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 type { SourceFile } from 'typescript'; 17import { SyntaxKind } from 'typescript'; 18import type { MethodEntity } from '../declaration-node/methodDeclaration'; 19import type { FunctionEntity } from '../declaration-node/functionDeclaration'; 20import type { MethodSignatureEntity } from '../declaration-node/methodSignatureDeclaration'; 21import { 22 generateSymbolIterator, 23 getCallbackStatement, 24 getReturnStatement, 25 getWarnConsole, 26 getReturnData, 27 getOverloadedFunctionCallbackStatement, 28 overloadedFunctionArr 29} from './generateCommonUtil'; 30 31interface MethodArrayProps { 32 methodArray: Array<MethodEntity>, 33 methodEntity: MethodEntity, 34 sourceFile: SourceFile, 35 mockApi: string, 36 methodBody: string 37} 38 39interface MethodArrayItemForEachProps { 40 returnSet: Set<string>, 41 value: MethodEntity | FunctionEntity | MethodSignatureEntity, 42 argSet: Set<string>, 43 isCallBack: boolean, 44 argParamsSet: string, 45 needOverloaded: boolean 46} 47 48interface MethodArrayBack { 49 returnSet: Set<string>, 50 methodBody: string, 51 isCallBack: boolean 52} 53 54/** 55 * generate class method 56 * @param rootName 57 * @param methodArray 58 * @param sourceFile 59 * @returns 60 */ 61export function generateCommonMethod( 62 rootName: string, 63 methodArray: Array<MethodEntity>, 64 sourceFile: SourceFile, 65 mockApi: string 66): string { 67 let methodBody = ''; 68 const methodEntity = methodArray[0]; 69 if (methodEntity.functionName.name === 'Symbol.iterator') { 70 methodBody += `this[${methodEntity.functionName.name}] = function(...args) {`; 71 methodBody += getWarnConsole(rootName, methodEntity.functionName.name); 72 methodBody += generateSymbolIterator(methodEntity); 73 methodBody += '};\n'; 74 return methodBody; 75 } else { 76 methodBody += `this.${methodEntity.functionName.name} = function(...args) {`; 77 methodBody += getWarnConsole(rootName, methodEntity.functionName.name); 78 } 79 80 if (methodArray.length === 1) { 81 const args = methodEntity.args; 82 const len = args.length; 83 if (args.length > 0 && args[len - 1].paramName.toLowerCase().includes('callback')) { 84 methodBody += getCallbackStatement(mockApi, args[len - 1]?.paramTypeString); 85 } 86 if (methodEntity.returnType.returnKind !== SyntaxKind.VoidKeyword) { 87 if (methodEntity.functionName.name === 'getApplicationContext') { 88 methodBody += getApplicationContextValue(methodEntity.functionName.name); 89 } else { 90 methodBody += getReturnStatement(methodEntity.returnType, sourceFile); 91 } 92 } 93 } else { 94 const methodArrayBack = methodArrayForEach({ methodArray, methodEntity, sourceFile, mockApi, methodBody }); 95 methodBody = returnSetForEach(methodArrayBack, methodArray, sourceFile, mockApi); 96 } 97 methodBody += '};\n'; 98 return methodBody; 99} 100 101function getApplicationContextValue(name: string): string { 102 return ` 103const mockData = { 104 on: function (...args) { 105 console.warn( 106 'The ${name}.on interface in the Previewer is a mocked implementation and may behave differently than on a real device.' 107 ); 108 if (args && ['environment'].includes(args[0])) { 109 if (args && typeof args[args.length - 1] === 'function') { 110 const EnvironmentCallback = mockEnvironmentCallback(); 111 args[args.length - 1].call(this, new EnvironmentCallback()); 112 } 113 } 114 return 0; 115 }, 116 off: function (...args) { 117 console.warn( 118 'The ${name}.off interface in the Previewer is a mocked implementation and may behave differently than on a real device.' 119 ); 120 if (args && ['environment'].includes(args[0])) { 121 if (args && typeof args[args.length - 1] === 'function') { 122 args[args.length - 1].call( 123 this, 124 { 'code': '', 'data': '', 'name': '', 'message': '', 'stack': '' }, 125 '[PC Preview] unknown type' 126 ); 127 } 128 } 129 return new Promise((resolve, reject) => { 130 resolve('[PC Preview] unknown type'); 131 }); 132 } 133}; 134return mockData; 135 `; 136} 137 138/** 139 * method Array ForEach 140 * @param props 141 * @returns 142 */ 143function methodArrayForEach(props: MethodArrayProps): MethodArrayBack { 144 let argSet: Set<string> = new Set<string>(); 145 let argParamsSet: string = ''; 146 let returnSet: Set<string> = new Set<string>(); 147 let isCallBack = false; 148 let needOverloaded = false; 149 props.methodArray.forEach(value => { 150 ({ returnSet, argSet, isCallBack, argParamsSet, needOverloaded} = 151 methodArrayItemForEach({returnSet, value, argSet, isCallBack, argParamsSet, needOverloaded})); 152 }); 153 if (isCallBack) { 154 if (overloadedFunctionArr.includes(props.methodEntity.functionName.name) && needOverloaded) { 155 props.methodBody += getOverloadedFunctionCallbackStatement(props.methodArray, props.sourceFile, props.mockApi); 156 } else { 157 props.methodBody += getCallbackStatement(props.mockApi, argParamsSet); 158 } 159 } 160 return { 161 returnSet, 162 methodBody: props.methodBody, 163 isCallBack 164 }; 165} 166 167/** 168 * method ArrayItem ForEach 169 * @param props 170 * @returns 171 */ 172export function methodArrayItemForEach( 173 props: MethodArrayItemForEachProps 174): MethodArrayItemForEachProps { 175 props.returnSet.add(props.value.returnType.returnKindName); 176 props.value.args.forEach(arg => { 177 props.argSet.add(arg.paramName); 178 if (arg.paramName.toLowerCase().includes('callback')) { 179 props.isCallBack = true; 180 if (arg.paramTypeString) { 181 props.argParamsSet = arg.paramTypeString; 182 } 183 } 184 if ( 185 arg.paramTypeString.startsWith("'") && arg.paramTypeString.endsWith("'") || 186 arg.paramTypeString.startsWith('"') && arg.paramTypeString.endsWith('"') 187 ) { 188 props.needOverloaded = true; 189 } 190 }); 191 return props; 192} 193 194/** 195 * returnSet ForEach 196 * @param props 197 * @param methodArray 198 * @param sourceFile 199 * @param mockApi 200 * @returns 201 */ 202function returnSetForEach( 203 props: MethodArrayBack, 204 methodArray: Array<MethodEntity>, 205 sourceFile: SourceFile, 206 mockApi: string 207): string { 208 let isReturnPromise = false; 209 let promiseReturnValue = ''; 210 let methodOtherReturnValue = ''; 211 props.returnSet.forEach(value => { 212 if (value.includes('Promise<')) { 213 isReturnPromise = true; 214 promiseReturnValue = value; 215 } else { 216 if (!methodOtherReturnValue) { 217 methodOtherReturnValue = value; 218 } 219 } 220 }); 221 if (isReturnPromise) { 222 if (promiseReturnValue) { 223 let returnType = null; 224 methodArray.forEach(value => { 225 if (value.returnType.returnKindName === promiseReturnValue) { 226 returnType = value.returnType; 227 } 228 }); 229 props.methodBody += getReturnData(props.isCallBack, isReturnPromise, returnType, sourceFile, mockApi); 230 } else { 231 props.methodBody += ` 232 return new Promise((resolve, reject) => { 233 resolve('[PC Preview] unknow boolean'); 234 }) 235 `; 236 } 237 } else if (methodOtherReturnValue) { 238 let returnType = null; 239 methodArray.forEach(value => { 240 if (value.returnType.returnKindName === methodOtherReturnValue) { 241 returnType = value.returnType; 242 } 243 }); 244 props.methodBody += getReturnData(props.isCallBack, isReturnPromise, returnType, sourceFile, mockApi); 245 } 246 return props.methodBody; 247} 248