• 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 fs from 'fs';
17import path from 'path';
18import { createSourceFile, ScriptTarget } from 'typescript';
19import {
20  getOhosInterfacesDir,
21  isDeclarationFile,
22  mkdirsSync,
23  generateKeyValue,
24  isNeedMocked
25} from './common/commonUtils';
26import { getSourceFileAssembly } from './declaration-node/sourceFileElementsAssemply';
27import { generateEntry } from './generate/generateEntry';
28import { generateIndex } from './generate/generateIndex';
29import { generateSystemIndex } from './generate/generateSystemIndex';
30import {
31  arktsDtsFileList,
32  componentDtsFileList,
33  D_ETS, D_TS,
34  KeyValueTypes,
35  kitsDtsFileList,
36  mockBufferMap,
37  MockedFileMap,
38  NO_CONTENT_FILES,
39  ohosDtsFileList,
40  RawFileMap
41} from './common/constants';
42import { ApiFolder, MockBuffer } from './types';
43import { generateContent, handleDeclares } from './generate/generateContent';
44
45/**
46 * 获取所有接口文件路径
47 * @param dir 接口目录路径
48 * @param fileList 接口文件列表
49 * @param isHmsDtsFile 是否是harmonyOS接口
50 * @returns
51 */
52function getAllDtsFile(dir: string, fileList: string[], isHmsDtsFile: boolean): string[] {
53  const arr = fs.readdirSync(dir);
54  if (!dir.toString().includes('node_modules') && !dir.toString().includes(path.join('@internal', 'component'))) {
55    arr.forEach(value => {
56      collectFile(dir, fileList, value, isHmsDtsFile);
57    });
58  }
59  return fileList;
60}
61
62function collectFile(dir: string, fileList: string[], value: string, isHmsDtsFile: boolean): void {
63  const fullPath = path.join(dir, value);
64  const stats = fs.statSync(fullPath);
65  if (stats.isDirectory()) {
66    getAllDtsFile(fullPath, fileList, isHmsDtsFile);
67  } else {
68    if (!isDeclarationFile(value)) {
69      return;
70    }
71    fileList.push(fullPath);
72  }
73}
74
75/**
76 * 获取所有组件接口文件路径
77 * @param dir 组件目录路径
78 * @returns
79 */
80function getAllComponentsFilePath(dir: string): Array<string> {
81  const componentPath = path.join(dir, '@internal', 'component', 'ets');
82  if (!fs.existsSync(componentPath)) {
83    return;
84  }
85  const componentPathArr = fs.readdirSync(componentPath);
86  componentPathArr.forEach(value => {
87    const fullPath = path.join(componentPath, value);
88    if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) {
89      if (!isDeclarationFile(value)) {
90        return;
91      }
92      componentDtsFileList.push(fullPath);
93    }
94  });
95}
96
97/**
98 * 获取所有global接口文件路径
99 * @param dir global目录路径
100 */
101function getAllGlobalFilePath(dir: string): Array<string> {
102  const globalPath = path.join(dir, 'global');
103  if (!fs.existsSync(globalPath)) {
104    return;
105  }
106  const globalPathArr = fs.readdirSync(globalPath);
107  globalPathArr.forEach(value => {
108    const fullPath = path.join(globalPath, value);
109    if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) {
110      if (!isDeclarationFile(value)) {
111        return;
112      }
113      ohosDtsFileList.push(fullPath);
114    }
115  });
116}
117
118/**
119 * 获取mock后的接口文件路径
120 * @param beforeMockedFilePath mock钱的文件路径
121 * @param apiFolder api目录路径
122 */
123function getMockedFileName(beforeMockedFilePath: string, apiFolder: ApiFolder): string {
124  const outMockJsFolder = path.join(__dirname, '..', '..', 'runtime', 'main', 'extend', 'systemplugin');
125  let fileName: string;
126  if (beforeMockedFilePath.endsWith(D_TS)) {
127    fileName = path.basename(beforeMockedFilePath, D_TS);
128  } else if (beforeMockedFilePath.endsWith(D_ETS)) {
129    fileName = path.basename(beforeMockedFilePath, D_ETS);
130  } else {
131    return '';
132  }
133
134  let tmpOutputMockJsFileDir = outMockJsFolder;
135  if (!fileName.startsWith('@system.')) {
136    tmpOutputMockJsFileDir = path.join(outMockJsFolder, 'napi', apiFolder);
137  }
138  let dirName: string;
139  switch (apiFolder) {
140    case 'api': {
141      dirName = path.join(tmpOutputMockJsFileDir, path.dirname(beforeMockedFilePath).split(`${path.sep}api`)[1]);
142      break;
143    }
144    case 'component': {
145      dirName = tmpOutputMockJsFileDir;
146      break;
147    }
148    case 'arkts': {
149      dirName = path.join(tmpOutputMockJsFileDir, path.dirname(beforeMockedFilePath).split(`${path.sep}arkts`)[1]);
150      break;
151    }
152    case 'kits': {
153      dirName = path.join(tmpOutputMockJsFileDir, path.dirname(beforeMockedFilePath).split(`${path.sep}kits`)[1]);
154      break;
155    }
156  }
157
158  if (!fs.existsSync(dirName)) {
159    mkdirsSync(dirName);
160  }
161  return path.join(dirName, fileName + '.js');
162}
163
164/**
165 * 初始化接口文件mock信息
166 * @param typeFile 类型文件路径
167 * @param apiFolder api目录路径
168 */
169function initialTypeFile(typeFile: string, apiFolder: ApiFolder): void {
170  if (!isDeclarationFile(typeFile)) {
171    return;
172  }
173  const filePath = getMockedFileName(typeFile, apiFolder);
174  if (mockBufferMap.get(typeFile)) {
175    return;
176  }
177  const mockBuffer: MockBuffer = {
178    rawFilePath: typeFile,
179    mockedFilePath: filePath,
180    contents: generateKeyValue(filePath, KeyValueTypes.FILE)
181  };
182  mockBufferMap.set(typeFile, mockBuffer);
183  RawFileMap.set(typeFile, filePath);
184  MockedFileMap.set(filePath, typeFile);
185}
186
187/**
188 * 处理所有ets文件的mock逻辑
189 */
190function etsFileToMock(): void {
191  componentDtsFileList.forEach(file => initialTypeFile(file, 'component'));
192  ohosDtsFileList.forEach(file => initialTypeFile(file, 'api'));
193  arktsDtsFileList.forEach(file => initialTypeFile(file, 'arkts'));
194  kitsDtsFileList.forEach(file => initialTypeFile(file, 'kits'));
195
196  const allFileList = [
197    ...componentDtsFileList,
198    ...ohosDtsFileList,
199    ...arktsDtsFileList,
200    ...kitsDtsFileList
201  ];
202
203  allFileList.forEach(file => {
204    const code = fs.readFileSync(file);
205    const sourceFile = createSourceFile(file, code.toString().replace(/ struct /g, ' class '), ScriptTarget.Latest);
206    const mockBuffer = mockBufferMap.get(file);
207    getSourceFileAssembly(sourceFile, mockBuffer, mockBuffer.contents.members, mockBuffer.contents);
208  });
209
210  handleDeclares(path.join(__dirname, '..', '..', 'runtime', 'main', 'extend', 'systemplugin'));
211
212  [
213    ...ohosDtsFileList,
214    ...arktsDtsFileList
215  ].forEach(filepath => {
216    if (!isDeclarationFile(filepath)) {
217      return;
218    }
219    if (!isNeedMocked(filepath)) {
220      return;
221    }
222    const mockBuffer = mockBufferMap.get(filepath);
223    const keyValue = mockBuffer.contents;
224    const mockedContents = generateContent(mockBuffer, keyValue.members);
225    if (!mockedContents.length) {
226      NO_CONTENT_FILES.add(filepath);
227    } else {
228      fs.writeFileSync(mockBuffer.mockedFilePath, mockedContents);
229    }
230  });
231}
232
233/**
234 * 项目入口函数
235 */
236function main(): void {
237  const outMockJsFileDir = path.join(__dirname, '..', '..', 'runtime', 'main', 'extend', 'systemplugin');
238  getAllGlobalFilePath(getOhosInterfacesDir());
239  getAllComponentsFilePath(getOhosInterfacesDir());
240  getAllDtsFile(getOhosInterfacesDir(), ohosDtsFileList, false);
241  getAllDtsFile(path.resolve(getOhosInterfacesDir(), '..', 'arkts'), arktsDtsFileList, false);
242  getAllDtsFile(path.resolve(getOhosInterfacesDir(), '..', 'kits'), kitsDtsFileList, false);
243
244  etsFileToMock();
245
246  if (!fs.existsSync(path.join(outMockJsFileDir, 'napi'))) {
247    mkdirsSync(path.join(outMockJsFileDir, 'napi'));
248  }
249  const napiIndexPath = path.join(outMockJsFileDir, 'napi', 'index.js');
250  fs.writeFileSync(napiIndexPath, generateIndex(napiIndexPath));
251  fs.writeFileSync(path.join(outMockJsFileDir, 'index.js'), generateSystemIndex());
252  fs.writeFileSync(path.join(outMockJsFileDir, 'entry.js'), generateEntry());
253}
254
255main();
256