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