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 (len && 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(props: MethodArrayItemForEachProps): MethodArrayItemForEachProps { 173 props.returnSet.add(props.value.returnType.returnKindName); 174 props.value.args.forEach(arg => { 175 props.argSet.add(arg.paramName); 176 if (arg.paramName.toLowerCase().includes('callback')) { 177 props.isCallBack = true; 178 if (arg.paramTypeString) { 179 props.argParamsSet = arg.paramTypeString; 180 } 181 } 182 if ( 183 arg.paramTypeString.startsWith("'") && arg.paramTypeString.endsWith("'") || 184 arg.paramTypeString.startsWith('"') && arg.paramTypeString.endsWith('"') 185 ) { 186 props.needOverloaded = true; 187 } 188 }); 189 return props; 190} 191 192/** 193 * returnSet ForEach 194 * @param props 195 * @param methodArray 196 * @param sourceFile 197 * @param mockApi 198 * @returns 199 */ 200function returnSetForEach( 201 props: MethodArrayBack, 202 methodArray: Array<MethodEntity>, 203 sourceFile: SourceFile, 204 mockApi: string 205): string { 206 let isReturnPromise = false; 207 let promiseReturnValue = ''; 208 let methodOtherReturnValue = ''; 209 props.returnSet.forEach(value => { 210 if (value.includes('Promise<')) { 211 isReturnPromise = true; 212 promiseReturnValue = value; 213 } else { 214 if (!methodOtherReturnValue) { 215 methodOtherReturnValue = value; 216 } 217 } 218 }); 219 if (isReturnPromise) { 220 if (promiseReturnValue) { 221 let returnType = null; 222 methodArray.forEach(value => { 223 if (value.returnType.returnKindName === promiseReturnValue) { 224 returnType = value.returnType; 225 } 226 }); 227 props.methodBody += getReturnData(props.isCallBack, isReturnPromise, returnType, sourceFile, mockApi); 228 } else { 229 props.methodBody += ` 230 return new Promise((resolve, reject) => { 231 resolve('[PC Preview] unknow boolean'); 232 }) 233 `; 234 } 235 } else if (methodOtherReturnValue) { 236 let returnType = null; 237 methodArray.forEach(value => { 238 if (value.returnType.returnKindName === methodOtherReturnValue) { 239 returnType = value.returnType; 240 } 241 }); 242 props.methodBody += getReturnData(props.isCallBack, isReturnPromise, returnType, sourceFile, mockApi); 243 } 244 return props.methodBody; 245} 246