• 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 path from 'path';
17import type {
18  CallSignatureDeclaration, ComputedPropertyName, FunctionDeclaration, Identifier, MethodDeclaration,
19  MethodSignature, ModifiersArray, ModuleDeclaration, NodeArray, ParameterDeclaration, PropertyName, SourceFile
20} from 'typescript';
21import {
22  isClassDeclaration, isComputedPropertyName, isIdentifier, isModuleBlock, isModuleDeclaration, isPrivateIdentifier
23} from 'typescript';
24import fs from 'fs';
25import type { ImportElementEntity } from '../declaration-node/importAndExportDeclaration';
26
27
28const paramIndex = 2;
29const allLegalImports = new Set<string>();
30const fileNameList = new Set<string>();
31const allClassSet = new Set<string>();
32
33export const dtsFileList: Array<string> = [];
34
35/**
36 * get all legal imports
37 * @returns
38 */
39export function getAllLegalImports(): Set<string> {
40  return allLegalImports;
41}
42
43/**
44 * get all legal imports
45 * @param element
46 */
47export function collectAllLegalImports(element: string): void {
48  allLegalImports.add(element);
49}
50
51/**
52 * collect all mock js file path
53 * @returns
54 */
55export function getAllFileNameList(): Set<string> {
56  return fileNameList;
57}
58
59/**
60 * collect all file name
61 */
62export function collectAllFileName(filePath: string): void {
63  const fullFileName = path.basename(filePath);
64  let fileName = '';
65  if (fullFileName.endsWith('d.ts')) {
66    fileName = fullFileName.split('.d.ts')[0];
67  } else if (fullFileName.endsWith('d.ets')) {
68    fileName = fullFileName.split('.d.ets')[0];
69  }
70
71  let outputFileName = '';
72  if (fileName.includes('@')) {
73    outputFileName = fileName.split('@')[1].replace(/\./g, '_');
74  } else {
75    outputFileName = fileName;
76  }
77  fileNameList.add(outputFileName);
78}
79
80/**
81 * get all class name set
82 * @returns
83 */
84export function getClassNameSet(): Set<string> {
85  return allClassSet;
86}
87
88/**
89 * get all class declaration
90 * @param sourceFile
91 * @returns
92 */
93export function getAllClassDeclaration(sourceFile: SourceFile): Set<string> {
94  sourceFile.forEachChild(node => {
95    if (isClassDeclaration(node)) {
96      if (node.name !== undefined) {
97        allClassSet.add(node.name.escapedText.toString());
98      }
99    } else if (isModuleDeclaration(node)) {
100      const moduleDeclaration = node as ModuleDeclaration;
101      const moduleBody = moduleDeclaration.body;
102      if (moduleBody !== undefined && isModuleBlock(moduleBody)) {
103        moduleBody.statements.forEach(value => {
104          if (isClassDeclaration(value)) {
105            if (value.name !== undefined) {
106              allClassSet.add(firstCharacterToUppercase(value.name?.escapedText.toString()));
107            }
108          }
109        });
110      }
111    }
112  });
113  return allClassSet;
114}
115
116/**
117 * get keywords
118 * @param modifiers
119 * @returns
120 */
121export function getModifiers(modifiers: ModifiersArray): Array<number> {
122  const modifiersArray: Array<number> = [];
123  modifiers.forEach(value => modifiersArray.push(value.kind));
124  return modifiersArray;
125}
126
127/**
128 * get property name
129 * @param node property node
130 * @param sourceFile
131 * @returns
132 */
133export function getPropertyName(node: PropertyName, sourceFile: SourceFile): string {
134  let propertyName = '';
135  if (isIdentifier(node) || isPrivateIdentifier(node)) {
136    const newNameNode = node as Identifier;
137    propertyName = newNameNode.escapedText.toString();
138  } else if (isComputedPropertyName(node)) {
139    const newNameNode = node as ComputedPropertyName;
140    propertyName = sourceFile.text.substring(newNameNode.expression.pos, newNameNode.expression.end).trimStart().trimEnd();
141  } else {
142    propertyName = sourceFile.text.substring(node.pos, node.end).trimStart().trimEnd();
143  }
144  return propertyName;
145}
146
147/**
148 * get parameter declaration
149 * @param parameter
150 * @param sourceFile
151 * @returns
152 */
153export function getParameter(parameter: ParameterDeclaration, sourceFile: SourceFile): ParameterEntity {
154  let paramName = '';
155  let paramTypeString = '';
156  const paramTypeKind = parameter.type?.kind === undefined ? -1 : parameter.type.kind;
157  if (isIdentifier(parameter.name)) {
158    paramName = parameter.name.escapedText === undefined ? '' : parameter.name.escapedText.toString();
159  } else {
160    const start = parameter.name.pos === undefined ? 0 : parameter.name.pos;
161    const end = parameter.name.end === undefined ? 0 : parameter.name.end;
162    paramName = sourceFile.text.substring(start, end).trimStart().trimEnd();
163  }
164
165  const start = parameter.type?.pos === undefined ? 0 : parameter.type.pos;
166  const end = parameter.type?.end === undefined ? 0 : parameter.type.end;
167  paramTypeString = sourceFile.text.substring(start, end).trimStart().trimEnd();
168  return {
169    paramName: paramName,
170    paramTypeString: paramTypeString,
171    paramTypeKind: paramTypeKind
172  };
173}
174
175/**
176 * get method or function return info
177 * @param node
178 * @param sourceFile
179 * @returns
180 */
181export function getFunctionAndMethodReturnInfo(node: FunctionDeclaration | MethodDeclaration |
182  MethodSignature | CallSignatureDeclaration, sourceFile: SourceFile): ReturnTypeEntity {
183  const returnInfo = { returnKindName: '', returnKind: -1 };
184  if (node.type !== undefined) {
185    const start = node.type.pos === undefined ? 0 : node.type.pos;
186    const end = node.type.end === undefined ? 0 : node.type.end;
187    returnInfo.returnKindName = sourceFile.text.substring(start, end).trimStart().trimEnd();
188    returnInfo.returnKind = node.type.kind;
189  }
190  return returnInfo;
191}
192
193/**
194 * get export modifiers
195 * @param modifiers
196 * @returns
197 */
198export function getExportKeyword(modifiers: ModifiersArray): Array<number> {
199  const modifiersArray: Array<number> = [];
200  modifiers.forEach(value => {
201    modifiersArray.push(value.kind);
202  });
203  return modifiersArray;
204}
205
206/**
207 *
208 * @param str first letter capitalization
209 * @returns
210 */
211export function firstCharacterToUppercase(str: string): string {
212  return str.slice(0, 1).toUpperCase() + str.slice(1);
213}
214
215/**
216 * parameters entity
217 */
218export interface ParameterEntity {
219  paramName: string,
220  paramTypeString: string,
221  paramTypeKind: number
222}
223
224/**
225 * return type entity
226 */
227export interface ReturnTypeEntity {
228  returnKindName: string,
229  returnKind: number
230}
231
232/**
233 * Get OpenHarmony project dir
234 * @return project dir
235 */
236
237export function getProjectDir(): string {
238  const apiInputPath = process.argv[paramIndex];
239  const privateInterface = path.join('vendor', 'huawei', 'interface', 'hmscore_sdk_js', 'api');
240  const openInterface = path.join('interface', 'sdk-js', 'api');
241  if (apiInputPath.indexOf(openInterface) > -1) {
242    return apiInputPath.replace(`${path.sep}${openInterface}`, '');
243  } else {
244    return apiInputPath.replace(`${path.sep}${privateInterface}`, '');
245  }
246}
247
248/**
249 * return interface api dir in OpenHarmony
250 */
251export function getOhosInterfacesDir(): string {
252  return path.join(getProjectDir(), 'interface', 'sdk-js', 'api');
253}
254
255/**
256 * return interface api root path
257 * @returns apiInputPath
258 */
259export function getApiInputPath(): string {
260  return process.argv[paramIndex];
261}
262
263/**
264 * return OpenHarmony file path dependent on by HarmonyOs
265 * @param importPath path of depend imported
266 * @param sourceFile sourceFile of current file
267 * @returns dependsFilePath
268 */
269export function findOhosDependFile(importPath: string, sourceFile: SourceFile): string {
270  const interFaceDir = getOhosInterfacesDir();
271  const tmpImportPath = importPath.replace(/'/g, '').replace('.d.ts', '').replace('.d.ets', '');
272  const sourceFileDir = path.dirname(sourceFile.fileName);
273  let dependsFilePath: string;
274  if (tmpImportPath.startsWith('./')) {
275    const subIndex = 2;
276    dependsFilePath = path.join(sourceFileDir, tmpImportPath.substring(subIndex));
277  } else if (tmpImportPath.startsWith('../')) {
278    const backSymbolList = tmpImportPath.split('/').filter(step => step === '..');
279    dependsFilePath = [
280      ...sourceFileDir.split(path.sep).slice(0, -backSymbolList.length),
281      ...tmpImportPath.split('/').filter(step => step !== '..')
282    ].join(path.sep);
283  } else if (tmpImportPath.startsWith('@ohos.inner.')) {
284    const pathSteps = tmpImportPath.replace(/@ohos\.inner\./g, '').split('.');
285    for (let i = 0; i < pathSteps.length; i++) {
286      const tmpInterFaceDir = path.join(interFaceDir, ...pathSteps.slice(0, i), pathSteps.slice(i).join('.'));
287      if (fs.existsSync(tmpInterFaceDir + '.d.ts')) {
288        return tmpInterFaceDir + '.d.ts';
289      }
290
291      if (fs.existsSync(tmpInterFaceDir + '.d.ets')) {
292        return tmpInterFaceDir + '.d.ets';
293      }
294    }
295  } else if (tmpImportPath.startsWith('@ohos.')) {
296    dependsFilePath = path.join(getOhosInterfacesDir(), tmpImportPath);
297  }
298
299  if (fs.existsSync(dependsFilePath + '.d.ts')) {
300    return dependsFilePath + '.d.ts';
301  }
302
303  if (fs.existsSync(dependsFilePath + '.d.ets')) {
304    return dependsFilePath + '.d.ets';
305  }
306
307  console.warn(`Cannot find module '${importPath}'`);
308  return '';
309}
310
311/**
312 * Determine if the file is a openHarmony interface file
313 * @param path: interface file path
314 * @returns
315 */
316export function isOhosInterface(path: string): boolean {
317  return path.startsWith(getOhosInterfacesDir());
318}
319
320/**
321 * reutn js-sdk root folder full path
322 * @returns
323 */
324export function getJsSdkDir(): string {
325  let sdkJsDir = process.argv[paramIndex].split(path.sep).slice(0, -1).join(path.sep);
326  sdkJsDir += sdkJsDir.endsWith(path.sep) ? '' : path.sep;
327  return sdkJsDir;
328}
329
330/**
331 * Determine whether the object has been imported
332 * @param importDeclarations imported Declaration list in current file
333 * @param typeName Object being inspected
334 * @returns
335 */
336export function hasBeenImported(importDeclarations: ImportElementEntity[], typeName: string): boolean {
337  if (!typeName.trim()) {
338    return true;
339  }
340  if (isFirstCharLowerCase(typeName)) {
341    return true;
342  }
343  return importDeclarations.some(importDeclaration => importDeclaration.importElements.includes(typeName));
344}
345
346/**
347 * Determine whether the first character in a string is a lowercase letter
348 * @param str target string
349 * @returns
350 */
351function isFirstCharLowerCase(str: string): boolean {
352  const lowerCaseFirstChar = str[0].toLowerCase();
353  return str[0] === lowerCaseFirstChar;
354}
355
356export const specialFiles = [
357  '@internal/component/ets/common.d.ts',
358  '@internal/component/ets/units.d.ts',
359  '@internal/component/ets/common_ts_ets_api.d.ts',
360  '@internal/component/ets/enums.d.ts',
361  '@internal/component/ets/alert_dialog.d.ts',
362  '@internal/component/ets/ability_component.d.ts'
363];