• 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).trim();
141  } else {
142    propertyName = sourceFile.text.substring(node.pos, node.end).trim();
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).trim();
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).trim();
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(
182  node: FunctionDeclaration | MethodDeclaration | MethodSignature | CallSignatureDeclaration,
183  sourceFile: SourceFile
184): ReturnTypeEntity {
185  const returnInfo = { returnKindName: '', returnKind: -1 };
186  if (node.type !== undefined) {
187    const start = node.type.pos === undefined ? 0 : node.type.pos;
188    const end = node.type.end === undefined ? 0 : node.type.end;
189    returnInfo.returnKindName = sourceFile.text.substring(start, end).trim();
190    returnInfo.returnKind = node.type.kind;
191  }
192  return returnInfo;
193}
194
195/**
196 * get export modifiers
197 * @param modifiers
198 * @returns
199 */
200export function getExportKeyword(modifiers: ModifiersArray): Array<number> {
201  const modifiersArray: Array<number> = [];
202  modifiers.forEach(value => {
203    modifiersArray.push(value.kind);
204  });
205  return modifiersArray;
206}
207
208/**
209 *
210 * @param str first letter capitalization
211 * @returns
212 */
213export function firstCharacterToUppercase(str: string): string {
214  return str.slice(0, 1).toUpperCase() + str.slice(1);
215}
216
217/**
218 * parameters entity
219 */
220export interface ParameterEntity {
221  paramName: string,
222  paramTypeString: string,
223  paramTypeKind: number
224}
225
226/**
227 * return type entity
228 */
229export interface ReturnTypeEntity {
230  returnKindName: string,
231  returnKind: number
232}
233
234/**
235 * Get OpenHarmony project dir
236 * @return project dir
237 */
238
239export function getProjectDir(): string {
240  const apiInputPath = process.argv[paramIndex];
241  const privateInterface = path.join('vendor', 'huawei', 'interface', 'hmscore_sdk_js', 'api');
242  const openInterface = path.join('interface', 'sdk-js', 'api');
243  if (apiInputPath.indexOf(openInterface) > -1) {
244    return apiInputPath.replace(`${path.sep}${openInterface}`, '');
245  } else {
246    return apiInputPath.replace(`${path.sep}${privateInterface}`, '');
247  }
248}
249
250/**
251 * return interface api dir in OpenHarmony
252 */
253export function getOhosInterfacesDir(): string {
254  return path.join(getProjectDir(), 'interface', 'sdk-js', 'api');
255}
256
257/**
258 * return interface api root path
259 * @returns apiInputPath
260 */
261export function getApiInputPath(): string {
262  return process.argv[paramIndex];
263}
264
265/**
266 * return OpenHarmony file path dependent on by HarmonyOs
267 * @param importPath path of depend imported
268 * @param sourceFile sourceFile of current file
269 * @returns dependsFilePath
270 */
271export function findOhosDependFile(importPath: string, sourceFile: SourceFile): string {
272  const interFaceDir = getOhosInterfacesDir();
273  const tmpImportPath = importPath.replace(/'/g, '').replace('.d.ts', '').replace('.d.ets', '');
274  const sourceFileDir = path.dirname(sourceFile.fileName);
275  let dependsFilePath: string;
276  if (tmpImportPath.startsWith('./')) {
277    const subIndex = 2;
278    dependsFilePath = path.join(sourceFileDir, tmpImportPath.substring(subIndex));
279  } else if (tmpImportPath.startsWith('../')) {
280    const backSymbolList = tmpImportPath.split('/').filter(step => step === '..');
281    dependsFilePath = [
282      ...sourceFileDir.split(path.sep).slice(0, -backSymbolList.length),
283      ...tmpImportPath.split('/').filter(step => step !== '..')
284    ].join(path.sep);
285  } else if (tmpImportPath.startsWith('@ohos.inner.')) {
286    const pathSteps = tmpImportPath.replace(/@ohos\.inner\./g, '').split('.');
287    for (let i = 0; i < pathSteps.length; i++) {
288      const tmpInterFaceDir = path.join(interFaceDir, ...pathSteps.slice(0, i), pathSteps.slice(i).join('.'));
289      if (fs.existsSync(tmpInterFaceDir + '.d.ts')) {
290        return tmpInterFaceDir + '.d.ts';
291      }
292
293      if (fs.existsSync(tmpInterFaceDir + '.d.ets')) {
294        return tmpInterFaceDir + '.d.ets';
295      }
296    }
297  } else if (tmpImportPath.startsWith('@ohos.')) {
298    dependsFilePath = path.join(getOhosInterfacesDir(), tmpImportPath);
299  }
300
301  if (fs.existsSync(dependsFilePath + '.d.ts')) {
302    return dependsFilePath + '.d.ts';
303  }
304
305  if (fs.existsSync(dependsFilePath + '.d.ets')) {
306    return dependsFilePath + '.d.ets';
307  }
308
309  console.warn(`Cannot find module '${importPath}'`);
310  return '';
311}
312
313/**
314 * Determine if the file is a openHarmony interface file
315 * @param path: interface file path
316 * @returns
317 */
318export function isOhosInterface(path: string): boolean {
319  return path.startsWith(getOhosInterfacesDir());
320}
321
322/**
323 * reutn js-sdk root folder full path
324 * @returns
325 */
326export function getJsSdkDir(): string {
327  let sdkJsDir = process.argv[paramIndex].split(path.sep).slice(0, -1).join(path.sep);
328  sdkJsDir += sdkJsDir.endsWith(path.sep) ? '' : path.sep;
329  return sdkJsDir;
330}
331
332/**
333 * Determine whether the object has been imported
334 * @param importDeclarations imported Declaration list in current file
335 * @param typeName Object being inspected
336 * @returns
337 */
338export function hasBeenImported(importDeclarations: ImportElementEntity[], typeName: string): boolean {
339  if (!typeName.trim()) {
340    return true;
341  }
342  if (isFirstCharLowerCase(typeName)) {
343    return true;
344  }
345  return importDeclarations.some(importDeclaration => importDeclaration.importElements.includes(typeName));
346}
347
348/**
349 * Determine whether the first character in a string is a lowercase letter
350 * @param str target string
351 * @returns
352 */
353function isFirstCharLowerCase(str: string): boolean {
354  const lowerCaseFirstChar = str[0].toLowerCase();
355  return str[0] === lowerCaseFirstChar;
356}
357
358export const specialFiles = [
359  '@internal/component/ets/common.d.ts',
360  '@internal/component/ets/units.d.ts',
361  '@internal/component/ets/common_ts_ets_api.d.ts',
362  '@internal/component/ets/enums.d.ts',
363  '@internal/component/ets/alert_dialog.d.ts',
364  '@internal/component/ets/ability_component.d.ts',
365  '@internal/component/ets/rich_editor.d.ts',
366  '@internal/component/ets/symbolglyph.d.ts',
367];
368