• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 { TypeAliasEntity, TypeAliasTypeEntity } from '../declaration-node/typeAliasDeclaration';
17import { firstCharacterToUppercase, getOhosInterfacesDir } from '../common/commonUtils';
18import path from 'path';
19import fs from 'fs';
20import type { SourceFile } from 'typescript';
21
22interface SubstepParseProps {
23  importFileContent: string,
24  realImportPath: string,
25  extraImport: string[],
26  properties: string
27}
28
29const interceptIndex = 2;
30
31/**
32 * generate type alias
33 * @param typeAliasEntity
34 * @param isInner
35 * @param sourceFile
36 * @param extraImport
37 * @returns
38 */
39export function generateTypeAliasDeclaration(
40  typeAliasEntity: TypeAliasEntity, isInner: boolean, sourceFile: SourceFile, extraImport: string[], mockApi: string
41): string {
42  let typeAliasName = '';
43  if (!isInner) {
44    typeAliasName += `export const ${typeAliasEntity.typeAliasName} = `;
45  } else {
46    typeAliasName += `const ${typeAliasEntity.typeAliasName} = `;
47  }
48
49  let typeAliasValue = '';
50
51  const typeAliasTypeElements = typeAliasEntity.typeAliasTypeElements;
52  if (sourceFile.fileName.endsWith('@ohos.abilityAccessCtrl.d.ts') && typeAliasEntity.typeAliasName === 'PermissionRequestResult') {
53    return typeAliasName + 'new _PermissionRequestResult();';
54  }
55
56  if (typeAliasTypeElements) {
57    typeAliasValue += parseImportExpression(typeAliasTypeElements, sourceFile, extraImport, mockApi);
58  }
59
60  if (!typeAliasValue) {
61    typeAliasValue += `'[PC Preview] unknown ${typeAliasEntity.typeAliasName}'`;
62  }
63  return typeAliasName + typeAliasValue + ';';
64}
65
66function getImportFileFullPath(typeName: string): string {
67  const importRelatePathTmp = typeName.match(/\('[^'()]+'\)/);
68  if (!importRelatePathTmp) {
69    return '';
70  }
71  const importRelatePath = importRelatePathTmp[0].substring(interceptIndex, importRelatePathTmp[0].length - interceptIndex);
72  const tmpRealPath = getOhosInterfacesDir() + importRelatePath.replace('../api', '').replace(/\//g, path.sep);
73  if (fs.existsSync(tmpRealPath + '.d.ts')) {
74    return tmpRealPath + '.d.ts';
75  }
76
77  if (fs.existsSync(tmpRealPath + '.d.ets')) {
78    return tmpRealPath + '.d.ets';
79  }
80  console.warn(`Can not find import \'${importRelatePath}\'`);
81  return '';
82}
83
84function pathToImportPath(currentFilePath: string, importFilePath: string): string {
85  const currentFilePathSteps = currentFilePath.replace(/.d.e?ts/, '').split('/');
86  const importFilePathSteps = importFilePath.replace(/.d.e?ts/, '').split(path.sep);
87  const importFilePathStepsLength = importFilePathSteps.length;
88  importFilePathSteps[importFilePathStepsLength - 1] = importFilePathSteps[importFilePathStepsLength - 1]
89    .replace('@', '').replace(/\./g, '_');
90  let differStepIndex: number;
91  for (differStepIndex = 0; differStepIndex < currentFilePathSteps.length; differStepIndex++) {
92    if (currentFilePathSteps[differStepIndex] !== importFilePathSteps[differStepIndex]) {
93      break;
94    }
95  }
96  const currentFileDifferPathSteps = currentFilePathSteps.slice(differStepIndex);
97  const importFileDifferPathSteps = importFilePathSteps.slice(differStepIndex);
98  if (currentFileDifferPathSteps.length === importFileDifferPathSteps.length && currentFileDifferPathSteps.length === 1) {
99    return `./${path.basename(importFilePath)}`;
100  } else {
101    const steps = [];
102    for (let i = 0; i < currentFileDifferPathSteps.length - 1; i++) {
103      steps.push('..');
104    }
105    const fullSteps = steps.concat(importFileDifferPathSteps);
106    return fullSteps.join('/');
107  }
108}
109
110function parseImportExpression(
111  typeAliasTypeElements: TypeAliasTypeEntity[], sourceFile: SourceFile, extraImport: string[], mockApi: string
112): string {
113  for (let i = 0; i < typeAliasTypeElements.length; i++) {
114    const typeAliasTypeElement = typeAliasTypeElements[i];
115    const typeName = typeAliasTypeElement.typeName;
116    if (!typeName) {
117      continue;
118    }
119    if (!typeName?.trim().startsWith('import(')) {
120      let name = typeName.trim();
121      if (name.includes('.')) {
122        name = name.trim().split('.')[0];
123      }
124      if (mockApi.includes(`import { ${name} `) || mockApi.includes(` as ${name.trim()} `) ||
125        mockApi.includes(`import ${name} `)) {
126        return typeName.trim();
127      }
128      continue;
129    }
130    const splitTypeName = typeName.split(')');
131    const propertiesIndex = 1;
132    let properties = splitTypeName[propertiesIndex];
133    if (properties.includes('<') && properties.includes('>')) {
134      properties = properties.replace(/<.*?>/sg, '');
135    }
136    const importPath = getImportFileFullPath(typeName);
137    const realImportPath = pathToImportPath(sourceFile.fileName, importPath);
138    if (!importPath) {
139      continue;
140    }
141    const importFileContent = fs.readFileSync(importPath, 'utf-8');
142    if (properties.startsWith('.default')) {
143      const result = substepParseImportExpression({importFileContent, realImportPath, extraImport, properties});
144      if (result !== '') {
145        return result;
146      }
147    } else {
148      const moduleName = properties.replace('.', '').split('.')[0];
149      const importStr = `import {${moduleName} as _${moduleName}} from '${realImportPath}';\n`;
150      !extraImport.includes(importStr) && extraImport.push(importStr);
151      return `_${properties.replace('.', '')}`;
152    }
153  }
154  return '';
155}
156
157/**
158 * generate type alias
159 * @param props
160 * @returns
161 */
162function substepParseImportExpression(props:SubstepParseProps): string {
163  let result = props.importFileContent.match(/export\sdefault\sclass\s[a-zA-Z]+/);
164  if (result) {
165    const defaultModuleName = '_' + result[0].replace(/export\sdefault\sclass\s/, '');
166    const importStr = `import ${defaultModuleName} from '${props.realImportPath}';\n`;
167    !props.extraImport.includes(importStr) && props.extraImport.push(importStr);
168    return `${defaultModuleName}${props.properties.replace('.default', '')}`;
169  }
170  result = props.importFileContent.match(/export\sdefault\s[a-zA-Z]+;/);
171  if (result) {
172    const moduleName = result[0].replace(/export\sdefault\s/, '').replace(';', '');
173    const mockFunctionName = `mock${firstCharacterToUppercase(moduleName)}`;
174    const importStr = `import {${mockFunctionName}} from '${props.realImportPath}';\n`;
175    !props.extraImport.includes(importStr) && props.extraImport.push(importStr);
176    return `${mockFunctionName}()${props.properties.replace('.default', '')}`;
177  }
178  return '';
179}
180
181