• 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 = '';
22const referencesMap = new Map();
23const referencesModuleMap = new Map();
24const kitFileNeedDeleteMap = new Map();
25/**
26 * @enum {string} references地址的切换类型
27 */
28const REFERENCE_TYPE = {
29  TOLOCAL: 'toLocal',
30  TORIGHTSDK: 'toRightSDK',
31  TOSDK: 'toSDK',
32};
33const PATT = {
34  GET_REFERENCE: /\/\/\/\s*<reference.*>/g,
35  GET_REFERENCEURL: /\/\/\/\s*<reference\s*path=("|')(.*)("|')\s*\/>/g,
36  REFERENCEURL_LOCAL: /(.\/)?(\S*)@internal\/component\/ets\/(\S*)/g,
37  REFERENCEURL_RIGHTSDK: /(..\/)(\S*)build-tools\/ets-loader\/declarations\/(\S*)/g,
38  REFERENCEURL_SDK: /(..\/)(\S*)component\/(\S*)/g,
39};
40function collectDeclaration(url) {
41  // 入口
42  try {
43    const utPath = path.resolve(__dirname, url);
44    const arktsPath = path.resolve(url, '../arkts');
45    const kitPath = path.resolve(url, '../kits');
46    const utFiles = [];
47    readFile(utPath, utFiles); // 读取文件
48    readFile(arktsPath, utFiles); // 读取文件
49    tsTransform(utFiles, deleteSystemApi);
50    tsTransformKitFile(kitPath);
51  } catch (error) {
52    console.error('DELETE_SYSTEM_PLUGIN ERROR: ', error);
53  }
54}
55
56/**
57 * 解析url目录下方的kit文件,删除对应systemapi
58 * @param { string } kitPath kit文件路径
59 */
60function tsTransformKitFile(kitPath) {
61  kitFileNeedDeleteMap.delete('');
62  if (kitFileNeedDeleteMap.length === 0) {
63    return;
64  }
65  const kitFiles = [];
66  readFile(kitPath, kitFiles); // 读取文件
67  kitFiles.forEach((kitFile) => {
68    const kitName = processFileNameWithoutExt(kitFile).replace('@kit.', '');
69    const content = fs.readFileSync(kitFile, 'utf-8');
70    const fileName = processFileName(kitFile);
71    let sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES2017, true);
72    const sourceInfo = getKitNewSourceFile(sourceFile, kitName);
73    if (isEmptyFile(sourceInfo.sourceFile)) {
74      return;
75    }
76    const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
77    let result = printer.printNode(ts.EmitHint.Unspecified, sourceInfo.sourceFile, sourceFile);
78    if (sourceInfo.copyrightMessage !== '') {
79      result = sourceInfo.copyrightMessage + result;
80    }
81    writeFile(kitFile, result);
82  });
83}
84
85/**
86 * 处理kit中需要删除的节点,在其他文件被systemapi修饰的api
87 * @param { ts.SourceFile } sourceFile
88 * @param { string } kitName
89 * @returns 删除完的节点,全部删除为空字符串
90 */
91function getKitNewSourceFile(sourceFile, kitName) {
92  const newStatements = [];
93  const needDeleteExportName = new Set();
94  const needDeleteMap = kitFileNeedDeleteMap.get(kitName);
95  let copyrightMessage = '';
96  // 初始化ts工厂
97  const factory = ts.factory;
98  sourceFile.statements.forEach((statement, index) => {
99    if (ts.isImportDeclaration(statement)) {
100      const newStatement = processKitImportDeclaration(statement, needDeleteMap, needDeleteExportName);
101      if (newStatement) {
102        newStatements.push(newStatement);
103      } else if (index === 0) {
104        copyrightMessage = sourceFile.getFullText().replace(sourceFile.getText(), '');
105      }
106    } else if (ts.isExportDeclaration(statement)) {
107      const exportSpecifiers = statement.exportClause.elements.filter((item) => {
108        return !needDeleteExportName.has(item.name.escapedText.toString());
109      });
110      if (exportSpecifiers.length !== 0) {
111        statement.exportClause = factory.updateNamedExports(statement.exportClause, exportSpecifiers);
112        newStatements.push(statement);
113      }
114    }
115  });
116  sourceFile = factory.updateSourceFile(sourceFile, newStatements);
117  return { sourceFile, copyrightMessage };
118}
119
120/**
121 * 根据节点和需要删除的节点数据生成新节点
122 * @param { ts.ImportDeclaration } statement 需要处理的import节点
123 * @param { Map} needDeleteMap 需要删除的节点数据
124 * @param { Map} needDeleteExportName 需要删除的导出节点
125 * @returns { ts.ImportDeclaration | undefined } 返回新的import节点,全部删除为undefined
126 */
127function processKitImportDeclaration(statement, needDeleteMap, needDeleteExportName) {
128  // 初始化ts工厂
129  const factory = ts.factory;
130  const importClause = statement.importClause;
131  if (!ts.isImportClause(importClause)) {
132    return statement;
133  }
134  const importPath = statement.moduleSpecifier.text.replace('../', '');
135  if (needDeleteMap === undefined || !needDeleteMap.has(importPath)) {
136    const hasFilePath = hasFileByImportPath(importPath);
137    return hasFilePath ? statement : undefined;
138  }
139  const currImportInfo = needDeleteMap.get(importPath);
140  let defaultName = '';
141  let importNodeNamedBindings = [];
142  if (importClause.name) {
143    if (currImportInfo.default === importClause.name.escapedText.toString()) {
144      //import buffer from "../@ohos.buffer";
145      needDeleteExportName.add(currImportInfo.default);
146    } else {
147      defaultName = importClause.name.escapedText.toString();
148    }
149  }
150  const namedBindings = importClause.namedBindings;
151  if (namedBindings !== undefined && ts.isNamedImports(namedBindings)) {
152    const elements = namedBindings.elements;
153    elements.forEach((element) => {
154      const exportName = element.propertyName ?
155        element.propertyName.escapedText.toString() :
156        element.name.escapedText.toString();
157      if (!currImportInfo.exportName.has(exportName)) {
158        importNodeNamedBindings.push(factory.createImportSpecifier(element.propertyName, element.name));
159      } else {
160        needDeleteExportName.add(element.name.escapedText.toString());
161      }
162    });
163  }
164  if (defaultName !== '' || importNodeNamedBindings.length !== 0) {
165    const newImportNode = factory.createImportDeclaration(
166      undefined,
167      undefined,
168      factory.createImportClause(
169        false,
170        defaultName === '' ? undefined : factory.createIdentifier(defaultName),
171        importNodeNamedBindings.length === 0 ? undefined : factory.createNamedImports(importNodeNamedBindings)
172      ),
173      statement.moduleSpecifier
174    );
175    return newImportNode;
176  }
177  return undefined;
178}
179
180/**
181 * 判断文件路径对应的文件是否存在
182 * @param {string} importPath kit文件import
183 * @returns {boolean} importPath是否存在
184 */
185function hasFileByImportPath(importPath) {
186  let fileDir = path.resolve(apiSourcePath);
187  if (importPath.startsWith('@arkts')) {
188    fileDir = path.resolve(apiSourcePath, '../arkts');
189  }
190  const flag = ['.d.ts', '.d.ets'].some(ext => {
191    const filePath = path.resolve(fileDir, `${importPath}${ext}`);
192    return fs.existsSync(filePath);
193  });
194  return flag;
195}
196
197/**
198 * 统一处理文件名称,修改后缀等
199 * @param {string} filePath 文件路径
200 * @returns {string} filename 文件名称
201 */
202function processFileName(filePath) {
203  return path
204    .basename(filePath)
205    .replace(/\.d\.ts$/g, '.ts')
206    .replace(/\.d\.ets$/g, '.ets');
207}
208
209function processFileNameWithoutExt(filePath) {
210  return path
211    .basename(filePath)
212    .replace(/\.d\.ts$/g, '')
213    .replace(/\.d\.ets$/g, '')
214    .replace(/\.ts$/g, '')
215    .replace(/\.ets$/g, '');
216}
217
218/**
219 * 遍历所有文件进行处理
220 * @param {Array} utFiles 所有文件
221 * @param {deleteSystemApi} callback 回调函数
222 */
223function tsTransform(utFiles, callback) {
224  utFiles.forEach((url) => {
225    const apiBaseName = path.basename(url);
226    if (/\.json/.test(url) || apiBaseName === 'index-full.d.ts') {
227      // 特殊类型文件处理
228      const content = fs.readFileSync(url, 'utf-8');
229      writeFile(url, content);
230    } else if (/\.d\.ts/.test(apiBaseName) || /\.d\.ets/.test(apiBaseName)) {
231      // dts文件处理
232      let content = fs.readFileSync(url, 'utf-8'); // 文件内容
233      const fileName = processFileName(url);
234      let references = content.match(PATT.GET_REFERENCE);
235      if (references) {
236        referencesMap.set(url, { references: references });
237        for (let index = 0; index < references.length; index++) {
238          const item = references[index];
239          content = content.replace(item, '');
240        }
241      }
242      ts.transpileModule(content, {
243        compilerOptions: {
244          target: ts.ScriptTarget.ES2017,
245        },
246        fileName: fileName,
247        transformers: { before: [callback(url)] },
248      });
249    }
250  });
251}
252/**
253 * 切换references或者references中path的格式
254 * @param {string} references references或者references中的path
255 * @param {REFERENCE_TYPE} reverse 切换类型
256 * @returns {string}
257 */
258function referencesToOthers(references, type) {
259  let referencesurl;
260  let hasFullpatt = references.match(PATT.GET_REFERENCEURL);
261  let isFullReferenceurl = hasFullpatt && hasFullpatt.length > 0;
262  if (isFullReferenceurl) {
263    referencesurl = RegExp.$2;
264  } else {
265    referencesurl = references;
266  }
267  let currentType = '';
268  if (referencesurl.match(PATT.REFERENCEURL_LOCAL)) {
269    currentType = REFERENCE_TYPE.TOLOCAL;
270  } else if (referencesurl.match(PATT.REFERENCEURL_RIGHTSDK)) {
271    currentType = REFERENCE_TYPE.TORIGHTSDK;
272  } else if (referencesurl.match(PATT.REFERENCEURL_SDK)) {
273    currentType = REFERENCE_TYPE.TOSDK;
274  }
275  if (currentType === '' || currentType === type) {
276    return references;
277  }
278  let starturl = '';
279  let fileName = '';
280  switch (currentType) {
281    case REFERENCE_TYPE.TOLOCAL:
282      starturl = RegExp.$2;
283      fileName = RegExp.$3;
284      break;
285    case REFERENCE_TYPE.TORIGHTSDK:
286      starturl = RegExp.$2;
287      fileName = RegExp.$3;
288      break;
289    case REFERENCE_TYPE.TOSDK:
290      starturl = RegExp.$2;
291      fileName = RegExp.$3;
292      break;
293    default:
294      break;
295  }
296  let finallyurl;
297  switch (type) {
298    case REFERENCE_TYPE.TOLOCAL:
299      finallyurl = `${starturl === '' ? './' : ''}${starturl}@internal/component/ets/${fileName}`;
300      break;
301    case REFERENCE_TYPE.TORIGHTSDK:
302      finallyurl = `../${starturl}build-tools/ets-loader/declarations/${fileName}`;
303      break;
304    case REFERENCE_TYPE.TOSDK:
305      finallyurl = `../${starturl}component/${fileName}`;
306      break;
307    default:
308      break;
309  }
310  if (isFullReferenceurl) {
311    finallyurl = `/// <reference path="${finallyurl}"/>`;
312  }
313  return finallyurl;
314}
315
316/**
317 * 读取目录下所有文件
318 * @param {string} dir 文件目录
319 * @param {Array} utFiles 所有文件
320 */
321function readFile(dir, utFiles) {
322  try {
323    const files = fs.readdirSync(dir);
324    files.forEach((element) => {
325      const filePath = path.join(dir, element);
326      const status = fs.statSync(filePath);
327      if (status.isDirectory()) {
328        readFile(filePath, utFiles);
329      } else {
330        utFiles.push(filePath);
331      }
332    });
333  } catch (e) {
334    console.log('ETS ERROR: ' + e);
335  }
336}
337
338function writeFile(url, data, option) {
339  if (fs.existsSync(outputPath)) {
340    fs.rmdirSync(outputPath, { recursive: true });
341  }
342  const newFilePath = path.resolve(outputPath, path.relative(__dirname, url));
343  fs.mkdir(path.dirname(newFilePath), { recursive: true }, (err) => {
344    if (err) {
345      console.log(`ERROR FOR CREATE PATH ${err}`);
346    } else {
347      if (data === '') {
348        fs.rmSync(newFilePath);
349        return;
350      }
351      fs.writeFileSync(newFilePath, data, option, (err) => {
352        if (err) {
353          console.log(`ERROR FOR CREATE FILE ${err}`);
354        }
355      });
356    }
357  });
358}
359
360const globalModules = new Map();
361
362/**
363 * 每个文件处理前回调函数第二个
364 * @param {string} url 文件路径
365 * @returns {Function}
366 */
367function formatImportDeclaration(url, copyrightMessage = '', isCopyrightDeleted = false) {
368  return (context) => {
369    const allIdentifierSet = new Set([]);
370    return (node) => {
371      sourceFile = node;
372      collectAllIdentifier(node); // 获取所有标识符
373      formatValue = formatAllNodes(url, node, allIdentifierSet); // 获取所有节点
374      node = formatValue.node;
375      const referencesMessage = formatValue.referencesMessage;
376      if (formatValue.isCopyrightDeleted) {
377        copyrightMessage = formatValue.copyrightMessage;
378        isCopyrightDeleted = formatValue.isCopyrightDeleted;
379      }
380      if (!isEmptyFile(node)) {
381        const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
382        let result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
383        if (isCopyrightDeleted) {
384          // 当第一个节点被删除时会同步删除整个文件jsdoc
385          result = copyrightMessage + '\n' + result;
386        }
387        copyrightMessage = node.getFullText().replace(node.getText(), '');
388        if (referencesMessage) {
389          // 将references写入文件
390          result =
391            result.substring(0, copyrightMessage.length) +
392            '\n' +
393            referencesMessage +
394            result.substring(copyrightMessage.length);
395        }
396        writeFile(url, result);
397      }
398      return node;
399    };
400    function collectAllIdentifier(node) {
401      if (ts.isSourceFile(node) && node.statements) {
402        node.statements.forEach((stat) => {
403          if (!ts.isImportDeclaration(stat)) {
404            ts.visitEachChild(stat, collectAllNodes, context);
405          }
406        });
407      }
408    }
409    function collectAllNodes(node) {
410      if (ts.isIdentifier(node)) {
411        allIdentifierSet.add(node.escapedText.toString());
412      }
413      return ts.visitEachChild(node, collectAllNodes, context);
414    }
415  };
416}
417
418function formatAllNodes(url, node, allIdentifierSet, copyrightMessage = '', isCopyrightDeleted = false) {
419  let referencesMessage = '';
420  let currReferencesModule = formatAllNodesReferences(url);
421  if (ts.isSourceFile(node) && node.statements) {
422    const newStatements = [];
423    node.statements.forEach((statement) => {
424      if (ts.isImportDeclaration(statement)) {
425        const importInfo = formatAllNodesImportDeclaration(
426          node,
427          statement,
428          url,
429          currReferencesModule,
430          allIdentifierSet
431        );
432        if (importInfo.statement) {
433          newStatements.push(statement);
434        } else if (importInfo.isCopyrightDeleted) {
435          copyrightMessage = importInfo.copyrightMessage;
436          isCopyrightDeleted = importInfo.isCopyrightDeleted;
437        }
438      } else if (ts.isStructDeclaration(statement)) {
439        statement = ts.factory.updateStructDeclaration(
440          statement,
441          statement.decorators,
442          statement.modifiers,
443          statement.name,
444          statement.typeParameters,
445          statement.heritageClauses,
446          statement.members.slice(1)
447        );
448        newStatements.push(statement);
449      } else {
450        newStatements.push(statement);
451      }
452    });
453    currReferencesModule.forEach((item) => {
454      if (item.isUsed) {
455        referencesMessage += item.reference + '\n';
456      }
457    });
458    node = ts.factory.updateSourceFile(node, newStatements);
459  }
460  return { node, referencesMessage, copyrightMessage, isCopyrightDeleted };
461}
462
463function hasCopyright(node) {
464  return /http\:\/\/www\.apache\.org\/licenses\/LICENSE\-2\.0/g.test(node.getFullText().replace(node.getText(), ''));
465}
466
467/**
468 * 处理References节点
469 * @param {ts.node} node 当前节点
470 * @param {string} url 文件路径
471 * @returns {Array} currReferencesModule 返回该文件的references数据
472 */
473function formatAllNodesReferences(url) {
474  globalModules.clear();
475  let currReferences = [];
476  let currReferencesModule = [];
477  if (referencesMap.has(url)) {
478    currReferences = referencesMap.get(url);
479    currReferencesModule = currReferences.references.map((element, index) => {
480      element.match(PATT.GET_REFERENCEURL);
481      let referencePath = RegExp.$2;
482      referencePath = referencesToOthers(referencePath, REFERENCE_TYPE.TOLOCAL);
483      let fullReferencePath = path.resolve(path.dirname(url), referencePath);
484      let module = referencesModuleMap.get(fullReferencePath);
485      for (const key in module) {
486        if (Object.hasOwnProperty.call(module, key)) {
487          globalModules.set(key, index);
488        }
489      }
490      return { modules: module, fullReferencePath: fullReferencePath, reference: element, isUsed: false };
491    });
492  }
493  return currReferencesModule;
494}
495
496/**
497 * 处理Import节点 去除未使用、不存在、References中没有对应模块的导入
498 * @param {ts.node} node 当前节点
499 * @param {ts.ImportDeclaration} statement 导入节点
500 * @param {string} url 文件路径
501 * @param {string} url 文件路径
502 * @param {Set} allIdentifierSet 该文件的所有Identifier关键字
503 * @returns {{statement:ts.ImportDeclaration,copyrightMessage:string,isCopyrightDeleted:boolean}} statement 处理完成的导入节点、copyrightMessage
504 */
505function formatAllNodesImportDeclaration(node, statement, url, currReferencesModule, allIdentifierSet) {
506  // 是import节点 import { AsyncCallback } from './@ohos.base';
507  const clauseSet = new Set([]);
508  if (statement.importClause && ts.isImportClause(statement.importClause)) {
509    // 标识符
510    const clauseNode = statement.importClause;
511    if (!clauseNode.namedBindings && clauseNode.name && ts.isIdentifier(clauseNode.name)) {
512      // 没有大括号的标识符
513      clauseSet.add(clauseNode.name.escapedText.toString());
514    } else if (
515      clauseNode.namedBindings &&
516      clauseNode.namedBindings.name &&
517      ts.isIdentifier(clauseNode.namedBindings.name)
518    ) {
519      // 没有标识符 *号
520      clauseSet.add(clauseNode.namedBindings.name.escapedText.toString());
521    } else if (clauseNode.namedBindings && clauseNode.namedBindings.elements) {
522      // 有花括号的标识符
523      clauseNode.namedBindings.elements.forEach((ele) => {
524        if (ele.name && ts.isIdentifier(ele.name)) {
525          clauseSet.add(ele.name.escapedText.toString());
526        }
527      });
528    }
529  }
530  const importSpecifier = statement.moduleSpecifier.getText().replace(/[\'\"]/g, '');
531  const dtsImportSpecifierPath = path.resolve(url, `../${importSpecifier}.d.ts`); // import 文件路径判断
532  const detsImportSpecifierPath = path.resolve(url, `../${importSpecifier}.d.ets`); // import 文件路径判断
533  let hasImportSpecifierFile = fs.existsSync(dtsImportSpecifierPath) || fs.existsSync(detsImportSpecifierPath);
534  let hasImportSpecifierInModules = globalModules.has(importSpecifier);
535  if ((hasImportSpecifierFile || hasImportSpecifierInModules) && clauseSet.size > 0) {
536    let currModule = [];
537    if (hasImportSpecifierInModules) {
538      let index = globalModules.get(importSpecifier);
539      currModule = currReferencesModule[index].modules[importSpecifier];
540    }
541    const clasueCheckList = [];
542    let exsitClauseSet = new Set([]);
543    for (const clause of clauseSet) {
544      let flag = allIdentifierSet.has(clause);
545      if (hasImportSpecifierInModules) {
546        flag = allIdentifierSet.has(clause) && currModule.includes(clause);
547      }
548      if (flag) {
549        // 标识符使用到了当前import中的引用
550        exsitClauseSet.add(clause);
551        clasueCheckList.push('exist');
552      } else {
553        clasueCheckList.push('non-exist');
554      }
555    }
556    let hasExsitStatus = false;
557    let hasNonExsitStatus = false;
558    clasueCheckList.forEach((ele) => {
559      if (ele === 'exist') {
560        hasExsitStatus = true;
561      } else {
562        hasNonExsitStatus = true;
563      }
564    });
565    if (hasExsitStatus) {
566      // 有使用到的标识符
567      if (hasNonExsitStatus) {
568        // 有没有使用到的标识符
569        const newSpecifiers = [];
570        statement.importClause.namedBindings.elements.forEach((element) => {
571          if (exsitClauseSet.has(element.name.escapedText.toString())) {
572            newSpecifiers.push(element);
573          }
574        });
575        statement.importClause.namedBindings = ts.factory.updateNamedImports(
576          statement.importClause.namedBindings,
577          newSpecifiers
578        );
579      }
580      if (hasImportSpecifierInModules) {
581        let index = globalModules.get(importSpecifier);
582        currReferencesModule[index].isUsed = true;
583      }
584      return { statement };
585    } else if (hasCopyright(statement)) {
586      return { copyrightMessage: node.getFullText().replace(node.getText(), ''), isCopyrightDeleted: true };
587    }
588  } else if (hasCopyright(statement)) {
589    return { copyrightMessage: node.getFullText().replace(node.getText(), ''), isCopyrightDeleted: true };
590  }
591  return { statement: undefined, copyrightMessage: '', isCopyrightDeleted: false };
592}
593
594/**
595 * 每个文件处理前回调函数第一个
596 * @callback deleteSystemApi
597 * @param {string} url 文件路径
598 * @returns {Function}
599 */
600function deleteSystemApi(url) {
601  return (context) => {
602    return (node) => {
603      const fullText = String(node.getFullText());
604      const copyrightMessage = fullText.replace(node.getText(), '').split(/\/\*\*/)[0];
605      let kitName = '';
606      if (fullText.match(/\@kit (.*)\r?\n/g)) {
607        kitName = RegExp.$1.replace(/\s/g, '');
608      }
609      sourceFile = node;
610      const deleteNode = processSourceFile(node, kitName); // 处理最外层节点
611      node = processVisitEachChild(context, deleteNode.node);
612      if (!isEmptyFile(node)) {
613        const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
614        const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
615        if (referencesMap.has(url)) {
616          resolveReferences(url);
617        }
618        const fileName = processFileName(url);
619        ts.transpileModule(result, {
620          compilerOptions: {
621            target: ts.ScriptTarget.ES2017,
622          },
623          fileName: fileName,
624          transformers: { before: [formatImportDeclaration(url, copyrightMessage, deleteNode.isCopyrightDeleted)] },
625        });
626      }
627      return node;
628    };
629  };
630}
631
632exports.deleteSystemApi = deleteSystemApi;
633
634/**
635 * 遍历每个文件下的所有节点,然后删除节点
636 * @param node
637 * @returns
638 */
639
640/**
641 * 处理最外层的节点看是否删除
642 * @param node 解析过后的节点
643 * @param kitName 当前文件kitName
644 * @returns
645 */
646function processSourceFile(node, kitName) {
647  let isCopyrightDeleted = false;
648  const newStatements = [];
649  const newStatementsWithoutExport = [];
650  const deleteSystemApiSet = new Set();
651  let needDeleteExport = {
652    fileName: '',
653    default: '',
654    exportName: new Set(),
655  };
656  isCopyrightDeleted = addNewStatements(node, newStatements, deleteSystemApiSet, needDeleteExport);
657  newStatements.forEach((statement) => {
658    const names = getExportIdentifierName(statement);
659    if (names.length === 0) {
660      newStatementsWithoutExport.push(statement);
661      return;
662    }
663    if (names.length === 1 && !deleteSystemApiSet.has(names[0])) {
664      //exports.name = test;
665      //export default test1
666      //export {test1}
667      newStatementsWithoutExport.push(statement);
668      return;
669    }
670    processExportNode(statement, node, needDeleteExport, names, deleteSystemApiSet, newStatementsWithoutExport);
671  });
672  if (needDeleteExport.fileName !== '') {
673    let kitMap = kitFileNeedDeleteMap.get(kitName);
674    if (kitMap === undefined) {
675      kitMap = new Map([[needDeleteExport.fileName, needDeleteExport]]);
676    } else {
677      kitMap.set(needDeleteExport.fileName, needDeleteExport);
678    }
679    kitFileNeedDeleteMap.set(kitName, kitMap);
680  }
681  return {
682    node: ts.factory.updateSourceFile(node, newStatementsWithoutExport, node.isDeclarationFile, node.referencedFiles),
683    isCopyrightDeleted,
684  };
685}
686
687function processExportNode(statement, node, needDeleteExport, names, deleteSystemApiSet, newStatementsWithoutExport) {
688  //删除export节点信息
689  if (ts.isExportAssignment(statement)) {
690    //export default abilityAccessCtrl;
691    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
692    needDeleteExport.default = statement.expression.escapedText.toString();
693  } else if (ts.isExportDeclaration(statement)) {
694    //export {test1 as test,testa as test2}
695    let needExport = false;
696    const newSpecifiers = [];
697    names.forEach((name, index) => {
698      if (!deleteSystemApiSet.has(name)) {
699        //未被删除的节点
700        newSpecifiers.push(statement.exportClause.elements[index]);
701        needExport = true;
702      } else {
703        //被删除的节点
704        needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
705        needDeleteExport.exportName.add(statement.name.escapedText.toString());
706      }
707    });
708    if (needExport) {
709      statement.exportClause = ts.factory.updateNamedExports(statement.exportClause, newSpecifiers);
710      newStatementsWithoutExport.push(statement);
711    }
712  }
713}
714
715function addNewStatements(node, newStatements, deleteSystemApiSet, needDeleteExport) {
716  let isCopyrightDeleted = false;
717  node.statements.forEach((statement, index) => {
718    if (!isSystemapi(statement)) {
719      newStatements.push(statement);
720      return;
721    }
722    if (index === 0) {
723      isCopyrightDeleted = true;
724    }
725    if (ts.isVariableStatement(statement)) {
726      deleteSystemApiSet.add(variableStatementGetEscapedText(statement));
727    } else if (
728      ts.isModuleDeclaration(statement) ||
729      ts.isInterfaceDeclaration(statement) ||
730      ts.isClassDeclaration(statement) ||
731      ts.isEnumDeclaration(statement) ||
732      ts.isStructDeclaration(statement) ||
733      ts.isTypeAliasDeclaration(statement)
734    ) {
735      if (statement && statement.name && statement.name.escapedText) {
736        deleteSystemApiSet.add(statement.name.escapedText.toString());
737      }
738      setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet);
739    } else if (ts.isExportAssignment(statement) || ts.isExportDeclaration(statement)) {
740      setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet);
741    }
742  });
743
744  return isCopyrightDeleted;
745}
746
747function setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet) {
748  if (ts.isExportAssignment(statement) && deleteSystemApiSet.has(statement.expression.escapedText.toString())) {
749    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
750    needDeleteExport.default = statement.expression.escapedText.toString();
751  } else if (ts.isExportDeclaration(statement)) {
752    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
753    statement.exportClause.elements.forEach((element) => {
754      const exportName = element.propertyName ?
755        element.propertyName.escapedText.toString() :
756        element.name.escapedText.toString();
757      if (deleteSystemApiSet.has(exportName)) {
758        needDeleteExport.exportName.add(element.name.escapedText.toString());
759      }
760    });
761  }
762  //export namespace test {}
763  const modifiers = statement.modifiers;
764  if (modifiers === undefined) {
765    return;
766  }
767  const exportFlag = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
768  const defaultFlag = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword);
769  if (exportFlag && defaultFlag) {
770    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
771    needDeleteExport.default = statement.name.escapedText.toString();
772  } else if (exportFlag) {
773    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
774    needDeleteExport.exportName.add(statement.name.escapedText.toString());
775  }
776}
777
778/**
779 * 获取export节点的名字,只获取第一个关键词
780 * @param {ts.node} statement
781 * @returns {Array<string>}
782 */
783function getExportIdentifierName(statement) {
784  const names = [];
785  if (ts.isExpressionStatement(statement)) {
786    //exports.name = test;
787    if (ts.isBinaryExpression(statement.expression)) {
788      names.push(statement.expression.right.escapedText.toString());
789    }
790  } else if (ts.isExportAssignment(statement)) {
791    //export default test1
792    names.push(statement.expression.escapedText.toString());
793  } else if (ts.isExportDeclaration(statement)) {
794    //export {test1} 、export {test1 as test} 、export * from './featureability'
795    const exportClause = statement.exportClause;
796    if (exportClause) {
797      const specifiers = exportClause.elements;
798      specifiers.forEach((specifier) => {
799        if (ts.isExportSpecifier(specifier)) {
800          const name = specifier.propertyName ? specifier.propertyName : specifier.name;
801          names.push(name.escapedText.toString());
802        }
803      });
804    }
805  }
806  return names;
807}
808
809/**
810 * 遍历处理tsnode节点
811 * @param  context 解析过后的内容
812 * @param  node 解析过后的节点
813 * @returns ts.node
814 */
815function processVisitEachChild(context, node) {
816  return ts.visitEachChild(node, processAllNodes, context); // 遍历所有子节点
817  function processAllNodes(node) {
818    if (ts.isInterfaceDeclaration(node)) {
819      const newMembers = [];
820      node.members.forEach((member) => {
821        if (!isSystemapi(member)) {
822          newMembers.push(member);
823        }
824      });
825      node = ts.factory.updateInterfaceDeclaration(
826        node,
827        node.decorators,
828        node.modifiers,
829        node.name,
830        node.typeParameters,
831        node.heritageClauses,
832        newMembers
833      );
834    } else if (ts.isClassDeclaration(node)) {
835      const newMembers = [];
836      node.members.forEach((member) => {
837        if (!isSystemapi(member)) {
838          newMembers.push(member);
839        }
840      });
841      node = ts.factory.updateClassDeclaration(
842        node,
843        node.decorators,
844        node.modifiers,
845        node.name,
846        node.typeParameters,
847        node.heritageClauses,
848        newMembers
849      );
850    } else if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) {
851      const newStatements = [];
852      node.body.statements.forEach((statement) => {
853        if (!isSystemapi(statement)) {
854          newStatements.push(statement);
855        }
856      });
857      const newModuleBody = ts.factory.updateModuleBlock(node.body, newStatements);
858      node = ts.factory.updateModuleDeclaration(node, node.decorators, node.modifiers, node.name, newModuleBody);
859    } else if (ts.isEnumDeclaration(node)) {
860      const newMembers = [];
861      node.members.forEach((member) => {
862        if (!isSystemapi(member)) {
863          newMembers.push(member);
864        }
865      });
866      node = ts.factory.updateEnumDeclaration(node, node.decorators, node.modifiers, node.name, newMembers);
867    } else if (ts.isStructDeclaration(node)) {
868      const newMembers = [];
869      node.members.forEach((member, index) => {
870        if (index >= 1 && !isSystemapi(member)) {
871          newMembers.push(member);
872        }
873      });
874      node = ts.factory.updateStructDeclaration(
875        node,
876        node.decorators,
877        node.modifiers,
878        node.name,
879        node.typeParameters,
880        node.heritageClauses,
881        newMembers
882      );
883    }
884    return ts.visitEachChild(node, processAllNodes, context);
885  }
886}
887
888/**
889 * 解析reference
890 * @param {string} url reference文件地址
891 */
892function resolveReferences(url) {
893  const obj = referencesMap.get(url);
894  let references = obj.references;
895  if (!references || references.length === 0) {
896    return;
897  }
898  for (let index = 0; index < references.length; index++) {
899    const element = references[index];
900    element.match(PATT.GET_REFERENCEURL);
901    let referencePath = RegExp.$2;
902    referencePath = referencesToOthers(referencePath, REFERENCE_TYPE.TOLOCAL);
903    let fullReferencePath = path.resolve(path.dirname(url), referencePath);
904    if (fs.existsSync(fullReferencePath) && !referencesModuleMap.has(fullReferencePath)) {
905      const content = fs.readFileSync(fullReferencePath, 'utf-8'); //文件内容
906      const fileName = processFileName(fullReferencePath);
907      ts.transpileModule(content, {
908        compilerOptions: {
909          target: ts.ScriptTarget.ES2017,
910        },
911        fileName: fileName,
912        transformers: { before: [resolveCallback(fullReferencePath)] },
913      });
914    }
915  }
916}
917function resolveCallback(url) {
918  return (context) => {
919    const allReferencesIdentifierSet = new Set([]);
920    let allModule = {};
921    return (node) => {
922      const referenceSourceFile = node;
923      node.statements.forEach((statement) => {
924        if (
925          ts.isModuleDeclaration(statement) &&
926          statement.name &&
927          ts.isStringLiteral(statement.name) &&
928          statement.body &&
929          ts.isModuleBlock(statement.body) &&
930          !isSystemapi(statement)
931        ) {
932          ts.visitEachChild(statement, collectAllIdentifier, context);
933          dealExternalStatements(referenceSourceFile);
934          allModule[statement.name.text.toString()] = [...allReferencesIdentifierSet];
935          allReferencesIdentifierSet.clear();
936        }
937      });
938      referencesModuleMap.set(url, allModule);
939      allModule = {};
940      return node;
941    };
942    function dealExternalStatements(node) {
943      node.statements.forEach((statement) => {
944        let name = '';
945        if (isSystemapi(statement)) {
946          if (ts.isVariableStatement(statement)) {
947            name = variableStatementGetEscapedText(statement);
948          } else if (
949            ts.isInterfaceDeclaration(statement) ||
950            ts.isClassDeclaration(statement) ||
951            ts.isEnumDeclaration(statement)
952          ) {
953            if (statement && statement.name && statement.name.escapedText) {
954              name = statement.name.escapedText.toString();
955            }
956          }
957          allReferencesIdentifierSet.delete(name);
958        }
959      });
960    }
961    function collectAllIdentifier(node) {
962      if (isSystemapi(node)) {
963        return;
964      }
965      if (ts.isIdentifier(node)) {
966        allReferencesIdentifierSet.add(node.escapedText.toString());
967      }
968      return ts.visitEachChild(node, collectAllIdentifier, context);
969    }
970  };
971}
972
973function variableStatementGetEscapedText(statement) {
974  let name = '';
975  if (
976    statement &&
977    statement.declarationList &&
978    statement.declarationList.declarations &&
979    statement.declarationList.declarations.length > 0 &&
980    statement.declarationList.declarations[0].name &&
981    statement.declarationList.declarations[0].name.escapedText
982  ) {
983    name = statement.declarationList.declarations[0].name.escapedText.toString();
984  }
985  return name;
986}
987
988function isSystemapi(node) {
989  const notesContent = node.getFullText().replace(node.getText(), '').replace(/[\s]/g, '');
990  const notesArr = notesContent.split(/\/\*\*/);
991  const notesStr = notesArr[notesArr.length - 1];
992  if (notesStr.length !== 0) {
993    return /@systemapi/g.test(notesStr);
994  }
995  return false;
996}
997
998function isEmptyFile(node) {
999  let isEmpty = true;
1000  if (ts.isSourceFile(node) && node.statements) {
1001    for (let i = 0; i < node.statements.length; i++) {
1002      const statement = node.statements[i];
1003      if (ts.isImportDeclaration(statement)) {
1004        continue;
1005      }
1006      isEmpty = false;
1007      break;
1008    }
1009  }
1010  return isEmpty;
1011}
1012
1013const apiSourcePath = '../api';
1014const outputPath = path.resolve(__dirname, 'output');
1015collectDeclaration(apiSourcePath); //入口
1016