• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2022 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 */
15const path = require('path');
16const fs = require('fs');
17const ts = require('typescript');
18
19let sourceFile = null;
20let lastNoteStr = '';
21let lastNodeName = '';
22
23function collectDeclaration(url) {
24  try {
25    const utPath = path.resolve(__dirname, url);
26    const utFiles = [];
27    readFile(utPath, utFiles);
28    tsTransform(utFiles, deleteSystemApi);
29  } catch (error) {
30    console.error("DELETE_SYSTEM_PLUGIN ERROR: ", error)
31  }
32}
33
34function tsTransform(utFiles, callback) {
35  utFiles.forEach((url) => {
36    if (/\.json/.test(url) || /index\-full\.d\.ts/.test(url) || /common\.d\.ts/.test(url)) {
37      const content = fs.readFileSync(url, 'utf-8');
38      writeFile(url, content);
39    } else if (/\.d\.ts/.test(url)) {
40      const content = fs.readFileSync(url, 'utf-8');
41      const fileName = path.basename(url).replace(/.d.ts/g, '.ts');
42      ts.transpileModule(content, {
43        compilerOptions: {
44          "target": ts.ScriptTarget.ES2017
45        },
46        fileName: fileName,
47        transformers: { before: [callback(url)] }
48      });
49    }
50  });
51}
52
53function readFile(dir, utFiles) {
54  try {
55    const files = fs.readdirSync(dir);
56    files.forEach((element) => {
57      const filePath = path.join(dir, element);
58      const status = fs.statSync(filePath);
59      if (status.isDirectory()) {
60        readFile(filePath, utFiles);
61      } else {
62        utFiles.push(filePath);
63      }
64    })
65  } catch (e) {
66    console.log('ETS ERROR: ' + e);
67  }
68}
69
70function writeFile(url, data, option) {
71  if (fs.existsSync(outputPath)) {
72    fs.rmdirSync(outputPath, { recursive: true });
73  }
74  const newFilePath = path.resolve(outputPath, path.relative(__dirname, url));
75  fs.mkdir(path.relative(__dirname, path.dirname(newFilePath)), { recursive: true }, (err) => {
76    if (err) {
77      console.log(`ERROR FOR CREATE PATH ${err}`);
78    } else {
79      fs.writeFile(newFilePath, data, option, (err) => {
80        if (err) {
81          console.log(`ERROR FOR CREATE FILE ${err}`);
82        }
83      })
84    }
85  })
86}
87
88const globalModules = new Set(['GlobalResource', 'StateManagement', 'SpecialEvent']);
89
90function formatImportDeclaration(url) {
91  return (context) => {
92    const allIdentifierSet = new Set([]);
93    let copyrightMessage = '';
94    let isCopyrightDeleted = false;
95    return (node) => {
96      sourceFile = node;
97      collectAllIdentifier(node);
98      node = formatAllNodes(node);
99      if (!isEmptyFile(node)) {
100        const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
101        let result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
102        if (isCopyrightDeleted) {
103          result = copyrightMessage + '\n' + result;
104        }
105        writeFile(url, result);
106      }
107      return node;
108    }
109    function collectAllIdentifier(node) {
110      if (ts.isSourceFile(node) && node.statements) {
111        node.statements.forEach(stat => {
112          if (!ts.isImportDeclaration(stat)) {
113            ts.visitEachChild(stat, collectAllNodes, context);
114          }
115        });
116      }
117    }
118    function collectAllNodes(node) {
119      if (ts.isIdentifier(node)) {
120        allIdentifierSet.add(node.escapedText.toString());
121      }
122      return ts.visitEachChild(node, collectAllNodes, context);
123    }
124    function formatAllNodes(node) {
125      if (ts.isSourceFile(node) && node.statements) {
126        const newStatements = [];
127        node.statements.forEach(statement => {
128          if (ts.isImportDeclaration(statement)) {
129            const clauseSet = new Set([]);
130            if (statement.importClause && ts.isImportClause(statement.importClause)) {
131              const clauseNode = statement.importClause;
132              if (!clauseNode.namedBindings && clauseNode.name && ts.isIdentifier(clauseNode.name)) {
133                clauseSet.add(clauseNode.name.escapedText.toString());
134              } else if (clauseNode.namedBindings && clauseNode.namedBindings.name &&
135                ts.isIdentifier(clauseNode.namedBindings.name)) {
136                clauseSet.add(clauseNode.namedBindings.name.escapedText.toString());
137              } else if (clauseNode.namedBindings && clauseNode.namedBindings.elements) {
138                clauseNode.namedBindings.elements.forEach(ele => {
139                  if (ele.name && ts.isIdentifier(ele.name)) {
140                    clauseSet.add(ele.name.escapedText.toString());
141                  }
142                });
143              }
144            }
145            const importSpecifier = statement.moduleSpecifier.getText().replace(/[\'\"]/g, '');
146            const importSpecifierRealPath = path.resolve(url, `../${importSpecifier}.d.ts`);
147            if ((fs.existsSync(importSpecifierRealPath) || globalModules.has(importSpecifier)) && clauseSet.size > 0) {
148              const clasueCheckList = [];
149              let exsitClauseSet = new Set([]);
150              for (const clause of clauseSet) {
151                if (allIdentifierSet.has(clause)) {
152                  exsitClauseSet.add(clause);
153                  clasueCheckList.push('exist');
154                } else {
155                  clasueCheckList.push('non-exist');
156                }
157              }
158              let hasExsitStatus = false;
159              let hasNonExsitStatus = false;
160              clasueCheckList.forEach(ele => {
161                if (ele === 'exist') {
162                  hasExsitStatus = true;
163                } else {
164                  hasNonExsitStatus = true;
165                }
166              });
167              if (hasExsitStatus) {
168                if (hasNonExsitStatus) {
169                  const newSpecifiers = [];
170                  statement.importClause.namedBindings.elements.forEach(element => {
171                    if (exsitClauseSet.has(element.name.escapedText.toString())) {
172                      newSpecifiers.push(element);
173                    }
174                  });
175                  statement.importClause.namedBindings = ts.factory.updateNamedImports(
176                    statement.importClause.namedBindings, newSpecifiers);
177                }
178                newStatements.push(statement);
179              } else if (hasCopyright(statement)) {
180                copyrightMessage = node.getFullText().replace(node.getText(), '');
181                isCopyrightDeleted = true;
182              }
183            } else if (hasCopyright(statement)) {
184              copyrightMessage = node.getFullText().replace(node.getText(), '');
185              isCopyrightDeleted = true;
186            }
187          } else {
188            newStatements.push(statement);
189          }
190        });
191        node = ts.factory.updateSourceFile(node, newStatements);
192      }
193      return node;
194    }
195    function hasCopyright(node) {
196      return /http\:\/\/www\.apache\.org\/licenses\/LICENSE\-2\.0/g.test(node.getFullText().replace(node.getText(), ''));
197    }
198  }
199}
200
201function deleteSystemApi(url) {
202  return (context) => {
203    return (node) => {
204      sourceFile = node;
205      node = processSourceFile(node);
206      node = ts.visitEachChild(node, processAllNodes, context);
207      if (!isEmptyFile(node)) {
208        const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
209        const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
210        const fileName = path.basename(url).replace(/.d.ts/g, '.ts');
211        ts.transpileModule(result, {
212          compilerOptions: {
213            "target": ts.ScriptTarget.ES2017
214          },
215          fileName: fileName,
216          transformers: { before: [formatImportDeclaration(url)] }
217        });
218      }
219      return node;
220    }
221    function processAllNodes(node) {
222      if (ts.isInterfaceDeclaration(node)) {
223        const newMembers = [];
224        node.members.forEach(member => {
225          if (!isSystemapi(member)) {
226            newMembers.push(member);
227          }
228        });
229        node = ts.factory.updateInterfaceDeclaration(node, node.modifiers, node.name,
230          node.typeParameters, node.heritageClauses, newMembers);
231      } else if (ts.isClassDeclaration(node)) {
232        const newMembers = [];
233        node.members.forEach(member => {
234          if (!isSystemapi(member)) {
235            newMembers.push(member);
236          }
237        });
238        node = ts.factory.updateClassDeclaration(node, node.modifiers, node.name,
239          node.typeParameters, node.heritageClauses, newMembers);
240      } else if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) {
241        const newStatements = [];
242        node.body.statements.forEach(statement => {
243          if (!isSystemapi(statement)) {
244            newStatements.push(statement);
245          }
246        });
247        const newModuleBody = ts.factory.updateModuleBlock(node.body, newStatements);
248        node = ts.factory.updateModuleDeclaration(node, node.modifiers, node.name, newModuleBody);
249      } else if (ts.isEnumDeclaration(node)) {
250        const newMembers = [];
251        node.members.forEach(member => {
252          if (!isSystemapi(member)) {
253            newMembers.push(member);
254          }
255        });
256        node = ts.factory.updateEnumDeclaration(node, node.modifiers, node.name, newMembers);
257      }
258      return ts.visitEachChild(node, processAllNodes, context);
259    }
260    function processSourceFile(node) {
261      const stateNamesSet = new Set([]);
262      const newStatements = [];
263      const newStatementsWithoutExport = [];
264      node.statements.forEach(statement => {
265        if (!isSystemapi(statement)) {
266          newStatements.push(statement);
267        } else if (ts.isModuleDeclaration(statement) && statement.name && ts.isIdentifier(statement.name)) {
268          stateNamesSet.add(statement.name.escapedText.toString());
269        }
270      });
271      newStatements.forEach(statement => {
272        if (!(ts.isExportAssignment(statement) && statement.expression && ts.isIdentifier(statement.expression) &&
273          stateNamesSet.has(statement.expression.escapedText.toString()))) {
274          newStatementsWithoutExport.push(statement);
275        }
276      });
277      return ts.factory.updateSourceFile(node, newStatementsWithoutExport, node.isDeclarationFile,
278        node.referencedFiles);
279    }
280  }
281}
282exports.deleteSystemApi = deleteSystemApi;
283
284function isSystemapi(node) {
285  const notesContent = node.getFullText().replace(node.getText(), "").replace(/[\s]/g, "");
286  const notesArr = notesContent.split(/\/\*\*/);
287  const notesStr = notesArr[notesArr.length - 1];
288  if (notesStr.length !== 0) {
289    if (ts.isFunctionDeclaration(node) || ts.isMethodSignature(node) || ts.isMethodDeclaration(node)) {
290      lastNodeName = node.name && node.name.escapedText ? node.name.escapedText.toString() : "";
291      lastNoteStr = notesStr;
292    }
293    return /\@systemapi/g.test(notesStr);
294  } else {
295    if ((ts.isFunctionDeclaration(node) || ts.isMethodSignature(node) || ts.isMethodDeclaration(node)) && node.name &&
296      node.name.escapedText.toString() !== "" && node.name.escapedText.toString() === lastNodeName) {
297      return /\@systemapi/g.test(lastNoteStr);
298    } else {
299      return false;
300    }
301  }
302}
303
304function isEmptyFile(node) {
305  if (ts.isSourceFile(node) && node.statements) {
306    for (let i = 0; i < node.statements.length; i++) {
307      const statement = node.statements[i];
308      if (!ts.isImportDeclaration(statement)) {
309        return false
310      }
311    }
312  }
313  return true;
314}
315
316const apiSourcePath = '../api';
317const outputPath = path.resolve(__dirname, 'output');
318collectDeclaration(apiSourcePath);