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 { SyntaxKind } from 'typescript';
17import type { SourceFile } from 'typescript';
18import type { PropertySignatureEntity } from '../declaration-node/propertySignatureDeclaration';
19import {
20  checkIsGenericSymbol,
21  getCallbackStatement,
22  getTheRealReferenceFromImport,
23  getWarnConsole,
24  propertyTypeWhiteList,
25  paramsTypeStart
26} from './generateCommonUtil';
27
28/**
29 * generate interface signature property
30 * @param rootName
31 * @param propertySignature
32 * @param sourceFile
33 * @returns
34 */
35export function generatePropertySignatureDeclaration(
36  rootName: string,
37  propertySignature: PropertySignatureEntity,
38  sourceFile: SourceFile,
39  mockApi: string
40): string {
41  let propertySignatureBody = '';
42  if (propertySignature.kind === SyntaxKind.FunctionType) {
43    propertySignatureBody += `${propertySignature.propertyName}: function(...args) {`;
44    propertySignatureBody += getWarnConsole(rootName, propertySignature.propertyName);
45    propertySignatureBody += getCallbackStatement(mockApi);
46    propertySignatureBody += '},\n';
47  } else {
48    if (
49      (propertySignature.propertyTypeName.startsWith('{') ||
50      propertySignature.propertyTypeName.startsWith('Record<') ||
51      propertySignature.propertyTypeName.startsWith('Object') ||
52      propertySignature.propertyTypeName.startsWith('object')) &&
53      !propertySignature.propertyTypeName.endsWith(']')
54    ) {
55      propertySignatureBody = `${propertySignature.propertyName}: {},`;
56    } else if (propertySignature.kind === SyntaxKind.TypeReference) {
57      propertySignatureBody = generatePropertySignatureForTypeReference(propertySignature, sourceFile);
58    } else if (propertySignature.kind === SyntaxKind.NumberKeyword) {
59      propertySignatureBody = `${propertySignature.propertyName}: 0,`;
60    } else if (propertySignature.kind === SyntaxKind.StringKeyword) {
61      propertySignatureBody = `${propertySignature.propertyName}: '[PC Preview] unknown ${propertySignature.propertyName}',`;
62    } else if (propertySignature.kind === SyntaxKind.BooleanKeyword) {
63      propertySignatureBody = `${propertySignature.propertyName}: true,`;
64    } else if (propertySignature.kind === SyntaxKind.UnionType) {
65      propertySignatureBody = generatePropertySignatureForUnionType(propertySignature, sourceFile);
66    } else if (propertySignature.kind === SyntaxKind.ArrayType) {
67      propertySignatureBody = `${propertySignature.propertyName}: [],`;
68    } else {
69      propertySignatureBody = `${propertySignature.propertyName}: '[PC Preview] unknown ${propertySignature.propertyName}',`;
70    }
71  }
72  return propertySignatureBody;
73}
74
75/**
76 * generate interface signature property for TypeReference
77 * @param propertySignature
78 * @param sourceFile
79 * @returns
80 */
81function generatePropertySignatureForTypeReference(
82  propertySignature: PropertySignatureEntity,
83  sourceFile: SourceFile
84): string {
85  let propertySignatureBody = '';
86  if (propertySignature.propertyTypeName.startsWith('Array')) {
87    propertySignatureBody = `${propertySignature.propertyName}: [],`;
88  } else if (propertySignature.propertyTypeName.startsWith('Map')) {
89    propertySignatureBody = `${propertySignature.propertyName}: {key: {}},`;
90  } else if (
91    propertySignature.propertyTypeName === 'string' ||
92    checkIsGenericSymbol(propertySignature.propertyTypeName) ||
93    propertySignature.propertyTypeName === 'bool' ||
94    propertySignature.propertyTypeName === 'Data'
95  ) {
96    propertySignatureBody = `${propertySignature.propertyName}: '[PC Preview] unknown ${propertySignature.propertyName}',`;
97  } else if (propertySignature.propertyTypeName === 'IlluminateType') {
98    propertySignatureBody = `${propertySignature.propertyName}: '',`;
99  } else {
100    if (propertySignature.propertyTypeName.includes('<')) {
101      propertySignatureBody = handlepropertyTypeNameBody(propertySignature, sourceFile);
102    } else {
103      if (propertyTypeWhiteList(propertySignature.propertyTypeName) === propertySignature.propertyTypeName) {
104        propertySignatureBody = `${propertySignature.propertyName}: ${getTheRealReferenceFromImport(sourceFile, propertySignature.propertyTypeName)},`;
105      } else {
106        propertySignatureBody = `${propertySignature.propertyName}: ${propertyTypeWhiteList(propertySignature.propertyTypeName)},`;
107      }
108    }
109  }
110  return propertySignatureBody;
111}
112
113/**
114 * generate interface signature property for UnionType
115 * @param propertySignature
116 * @param sourceFile
117 * @returns
118 */
119function generatePropertySignatureForUnionType(
120  propertySignature: PropertySignatureEntity,
121  sourceFile: SourceFile
122): string {
123  let propertySignatureBody = '';
124  let unionFirstElement = propertySignature.propertyTypeName.split('|')[0].trim();
125  if (unionFirstElement.includes('[]') || unionFirstElement.startsWith('[') || unionFirstElement.endsWith(']')) {
126    unionFirstElement = '[]';
127  }
128  if (unionFirstElement.startsWith('"') || unionFirstElement.startsWith("'")) {
129    propertySignatureBody = `${propertySignature.propertyName}: ${unionFirstElement},`;
130  } else if (unionFirstElement === 'string') {
131    propertySignatureBody = `${propertySignature.propertyName}: '[PC Preview] unknown ${propertySignature.propertyName}',`;
132  } else if (unionFirstElement === 'number') {
133    propertySignatureBody = `${propertySignature.propertyName}: 0,`;
134  } else if (unionFirstElement === 'boolean') {
135    propertySignatureBody = `${propertySignature.propertyName}: true,`;
136  } else if (unionFirstElement === 'Uint8Array') {
137    propertySignatureBody = `${propertySignature.propertyName}: new ${unionFirstElement}(),`;
138  } else {
139    let element = unionFirstElement;
140    if (element === 'HTMLCanvasElement') {
141      element = `'[PC Preview] unknown ${propertySignature.propertyName}'`;
142    } else if (element === 'WebGLActiveInfo') {
143      element = '{size: \'[PC Preview] unknown GLint\', type: 0, name: \'[PC Preview] unknown name\'}';
144    } else if (element.startsWith('Array')) {
145      element = '[]';
146    } else if (propertyTypeWhiteList(unionFirstElement) === unionFirstElement) {
147      element = getTheRealReferenceFromImport(sourceFile, unionFirstElement);
148    }
149    propertySignatureBody = `${propertySignature.propertyName}: ${element},`;
150  }
151  return propertySignatureBody;
152}
153
154/**
155 * generate interface signature property for TypeReference
156 * @param propertySignature
157 * @param sourceFile
158 * @returns
159 */
160function handlepropertyTypeNameBody(propertySignature: PropertySignatureEntity, sourceFile: SourceFile): string {
161  let propertySignatureBody = '';
162  if (
163    propertySignature.propertyTypeName.startsWith('AsyncCallback') ||
164    propertySignature.propertyTypeName.startsWith('Callback')
165  ) {
166    propertySignatureBody = `${propertySignature.propertyName}: ()=>{},`;
167  } else {
168    const preSplit = propertySignature.propertyTypeName.split('<');
169    const genericArg = preSplit[preSplit.length - 1].split('>')[0];
170    if (genericArg.includes(',')) {
171      return `${propertySignature.propertyName}: {},`;
172    }
173    let sourceFileContent = sourceFile.text;
174    const removeNoteRegx = /\/\*[\s\S]*?\*\//g;
175    sourceFileContent = sourceFileContent.replace(removeNoteRegx, '');
176    const regex = new RegExp(`\\s${genericArg}\\s({|=|extends)`);
177    const results = sourceFileContent.match(regex);
178    if (results) {
179      propertySignatureBody = `${propertySignature.propertyName}: ${genericArg},`;
180    } else {
181      let getPropertyTypeName = null;
182      Object.keys(paramsTypeStart).forEach(key => {
183        if (genericArg.startsWith(key)) {
184          getPropertyTypeName =
185            paramsTypeStart[key] === '[PC Preview] unknown type'
186              ? `'${paramsTypeStart[key]}'`
187              : `${paramsTypeStart[key]}`;
188        }
189      });
190      propertySignatureBody = `${propertySignature.propertyName}: ${
191        getPropertyTypeName ?? '\'[PC Preview] unknown type\''
192      },`;
193    }
194  }
195  return propertySignatureBody;
196}
197