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