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  collectAllFileName,
21  getAllClassDeclaration,
22  dtsFileList,
23  getOhosInterfacesDir,
24  specialFiles,
25  generateKitMap,
26  generateDependJsonFile,
27  generateMyComponent
28} from './common/commonUtils';
29import { getSourceFileAssembly } from './declaration-node/sourceFileElementsAssemply';
30import { generateEntry } from './generate/generateEntry';
31import { generateIndex } from './generate/generateIndex';
32import { generateSourceFileElements } from './generate/generateMockJsFile';
33import { generateSystemIndex } from './generate/generateSystemIndex';
34
35/**
36 * get all api .d.ts file path
37 * @param dir
38 * @returns
39 */
40function getAllDtsFile(dir: string): void {
41  const arr = fs.readdirSync(dir);
42  if (!dir.toString().includes('node_modules') && !dir.toString().includes(path.join('@internal', 'component'))) {
43    arr.forEach(value => {
44      const fullPath = path.join(dir, value);
45      const stats = fs.statSync(fullPath);
46      if (stats.isDirectory()) {
47        getAllDtsFile(fullPath);
48      } else {
49        dtsFileList.push(fullPath);
50      }
51    });
52  }
53}
54
55/**
56 * get all component .d.ts file path
57 * @param dir
58 * @returns
59 */
60function getAllComponentsFilePath(dir: string): Array<string> {
61  const componentPath = path.join(dir, '@internal', 'component', 'ets');
62  if (!fs.existsSync(componentPath)) {
63    return;
64  }
65  const componentPathArr = fs.readdirSync(componentPath);
66  componentPathArr.forEach(value => {
67    const fullPath = path.join(componentPath, value);
68    if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) {
69      const componentName = `@internal/component/ets/${value}`;
70      if (!specialFiles.includes(componentName)) {
71        specialFiles.push(componentName);
72      }
73    }
74  });
75}
76
77/**
78 * mkdir
79 * @param dirname
80 * @returns
81 */
82function mkdirsSync(dirname): boolean {
83  if (fs.existsSync(dirname)) {
84    return true;
85  } else {
86    if (mkdirsSync(path.dirname(dirname))) {
87      fs.mkdirSync(dirname);
88      return true;
89    }
90  }
91  return false;
92}
93
94/**
95 * hgandle all ets file mock logic
96 * @param outMockJsFileDir automated mock file output path
97 * @returns
98 */
99function etsFileToMock(outMockJsFileDir: string): void {
100  let index = 0;
101  while (index < dtsFileList.length) {
102    const value = dtsFileList[index];
103    index++;
104
105    if (!value.endsWith('.d.ts') && !value.endsWith('.d.ets')) {
106      continue;
107    }
108
109    const code = fs.readFileSync(value);
110    const sourceFile = createSourceFile(value, code.toString(), ScriptTarget.Latest);
111    let fileName: string;
112    if (value.endsWith('.d.ts')) {
113      fileName = path.basename(value, '.d.ts');
114    } else if (value.endsWith('.d.ets')) {
115      fileName = path.basename(value, '.d.ets');
116    } else {
117      continue;
118    }
119    let outputFileName = '';
120    if (fileName.includes('@')) {
121      outputFileName = fileName.split('@')[1].replace(/\./g, '_');
122    } else {
123      outputFileName = fileName;
124    }
125
126    let tmpOutputMockJsFileDir = outMockJsFileDir;
127    if (!outputFileName.startsWith('system_')) {
128      tmpOutputMockJsFileDir = path.join(outMockJsFileDir, 'napi');
129    }
130
131    if (value.startsWith(getOhosInterfacesDir()) && !apiInputPath.startsWith(getOhosInterfacesDir())) {
132      tmpOutputMockJsFileDir = path.join(tmpOutputMockJsFileDir, '@ohos');
133    }
134
135    let dirName = '';
136    dirName = path.join(tmpOutputMockJsFileDir, path.dirname(value).split(`${path.sep}api`)[1]);
137    if (!fs.existsSync(dirName)) {
138      mkdirsSync(dirName);
139    }
140    const sourceFileEntity = getSourceFileAssembly(sourceFile, fileName);
141    const filePath = path.join(dirName, outputFileName + '.js');
142    fs.writeFileSync(filePath, '');
143    fs.appendFileSync(
144      path.join(filePath),
145      generateSourceFileElements('', sourceFileEntity, sourceFile, outputFileName)
146    );
147  }
148}
149
150/**
151 * Project Entry Function
152 * @param apiInputPath interface_sdk-js\api absolute file path
153 * @returns
154 */
155function main(apiInputPath): void {
156  const dtsDir = apiInputPath;
157  const outMockJsFileDir = path.join(__dirname, '../../runtime/main/extend/systemplugin');
158  generateKitMap(apiInputPath);
159  getAllDtsFile(dtsDir);
160  getAllComponentsFilePath(dtsDir);
161  dtsFileList.forEach(value => {
162    collectAllFileName(value);
163    if (value.endsWith('.d.ts') || value.endsWith('.d.ets')) {
164      const code = fs.readFileSync(value);
165      const sourceFile = createSourceFile(value, code.toString(), ScriptTarget.Latest);
166      getAllClassDeclaration(sourceFile);
167    }
168  });
169
170  etsFileToMock(outMockJsFileDir);
171
172  if (!fs.existsSync(path.join(outMockJsFileDir, 'napi'))) {
173    mkdirsSync(path.join(outMockJsFileDir, 'napi'));
174  }
175  generateDependJsonFile();
176  generateMyComponent(outMockJsFileDir);
177  fs.writeFileSync(path.join(outMockJsFileDir, 'napi', 'index.js'), generateIndex());
178  fs.writeFileSync(path.join(outMockJsFileDir, 'index.js'), generateSystemIndex());
179  fs.writeFileSync(path.join(outMockJsFileDir, 'entry.js'), generateEntry());
180}
181
182const paramIndex = 2;
183const apiInputPath = process.argv[paramIndex];
184main(apiInputPath);
185