• 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 * 防止@file和@kit段注释丢失
597 * @param {string} fileFullText
598 * @returns {string}
599 *
600 */
601function getFileAndKitComment(fileFullText) {
602  let fileAndKitComment = '';
603  let pattern = /\/\*\*\s*\*\s*@file[\s\S]*?@kit[\s\S]*?\*\//;
604  let comment = fileFullText.match(pattern);
605  if (comment) {
606    fileAndKitComment = comment[0];
607  }
608  return fileAndKitComment;
609}
610
611
612/**
613 * 每个文件处理前回调函数第一个
614 * @callback deleteSystemApi
615 * @param {string} url 文件路径
616 * @returns {Function}
617 */
618function deleteSystemApi(url) {
619  return (context) => {
620    return (node) => {
621      const fullText = String(node.getFullText());
622      //获取文件头部的注释信息--这里可能会涉及到@file和@kit段注释丢失
623      let fileAndKitComment = getFileAndKitComment(fullText);
624      const copyrightMessage = fullText.replace(node.getText(), '').split(/\/\*\*/)[0] + fileAndKitComment + '\n';
625      let kitName = '';
626      if (fullText.match(/\@kit (.*)\r?\n/g)) {
627        kitName = RegExp.$1.replace(/\s/g, '');
628      }
629      sourceFile = node;
630      const deleteNode = processSourceFile(node, kitName); // 处理最外层节点
631      node = processVisitEachChild(context, deleteNode.node);
632      if (!isEmptyFile(node)) {
633        const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
634        const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
635        if (referencesMap.has(url)) {
636          resolveReferences(url);
637        }
638        const fileName = processFileName(url);
639        ts.transpileModule(result, {
640          compilerOptions: {
641            target: ts.ScriptTarget.ES2017,
642          },
643          fileName: fileName,
644          transformers: { before: [formatImportDeclaration(url, copyrightMessage, deleteNode.isCopyrightDeleted)] },
645        });
646      }
647      return node;
648    };
649  };
650}
651
652exports.deleteSystemApi = deleteSystemApi;
653
654/**
655 * 遍历每个文件下的所有节点,然后删除节点
656 * @param node
657 * @returns
658 */
659
660/**
661 * 处理最外层的节点看是否删除
662 * @param node 解析过后的节点
663 * @param kitName 当前文件kitName
664 * @returns
665 */
666function processSourceFile(node, kitName) {
667  let isCopyrightDeleted = false;
668  const newStatements = [];
669  const newStatementsWithoutExport = [];
670  const deleteSystemApiSet = new Set();
671  let needDeleteExport = {
672    fileName: '',
673    default: '',
674    exportName: new Set(),
675  };
676  isCopyrightDeleted = addNewStatements(node, newStatements, deleteSystemApiSet, needDeleteExport);
677  newStatements.forEach((statement) => {
678    const names = getExportIdentifierName(statement);
679    if (names.length === 0) {
680      newStatementsWithoutExport.push(statement);
681      return;
682    }
683    if (names.length === 1 && !deleteSystemApiSet.has(names[0])) {
684      //exports.name = test;
685      //export default test1
686      //export {test1}
687      newStatementsWithoutExport.push(statement);
688      return;
689    }
690    processExportNode(statement, node, needDeleteExport, names, deleteSystemApiSet, newStatementsWithoutExport);
691  });
692  if (needDeleteExport.fileName !== '') {
693    let kitMap = kitFileNeedDeleteMap.get(kitName);
694    if (kitMap === undefined) {
695      kitMap = new Map([[needDeleteExport.fileName, needDeleteExport]]);
696    } else {
697      kitMap.set(needDeleteExport.fileName, needDeleteExport);
698    }
699    kitFileNeedDeleteMap.set(kitName, kitMap);
700  }
701  return {
702    node: ts.factory.updateSourceFile(node, newStatementsWithoutExport, node.isDeclarationFile, node.referencedFiles),
703    isCopyrightDeleted,
704  };
705}
706
707function processExportNode(statement, node, needDeleteExport, names, deleteSystemApiSet, newStatementsWithoutExport) {
708  //删除export节点信息
709  if (ts.isExportAssignment(statement)) {
710    //export default abilityAccessCtrl;
711    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
712    needDeleteExport.default = statement.expression.escapedText.toString();
713  } else if (ts.isExportDeclaration(statement)) {
714    //export {test1 as test,testa as test2}
715    let needExport = false;
716    const newSpecifiers = [];
717    names.forEach((name, index) => {
718      if (!deleteSystemApiSet.has(name)) {
719        //未被删除的节点
720        newSpecifiers.push(statement.exportClause.elements[index]);
721        needExport = true;
722      } else {
723        //被删除的节点
724        needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
725        needDeleteExport.exportName.add(statement.name.escapedText.toString());
726      }
727    });
728    if (needExport) {
729      statement.exportClause = ts.factory.updateNamedExports(statement.exportClause, newSpecifiers);
730      newStatementsWithoutExport.push(statement);
731    }
732  }
733}
734
735function addNewStatements(node, newStatements, deleteSystemApiSet, needDeleteExport) {
736  let isCopyrightDeleted = false;
737  node.statements.forEach((statement, index) => {
738    if (!isSystemapi(statement)) {
739      newStatements.push(statement);
740      return;
741    }
742    if (index === 0) {
743      isCopyrightDeleted = true;
744    }
745    if (ts.isVariableStatement(statement)) {
746      deleteSystemApiSet.add(variableStatementGetEscapedText(statement));
747    } else if (
748      ts.isModuleDeclaration(statement) ||
749      ts.isInterfaceDeclaration(statement) ||
750      ts.isClassDeclaration(statement) ||
751      ts.isEnumDeclaration(statement) ||
752      ts.isStructDeclaration(statement) ||
753      ts.isTypeAliasDeclaration(statement)
754    ) {
755      if (statement && statement.name && statement.name.escapedText) {
756        deleteSystemApiSet.add(statement.name.escapedText.toString());
757      }
758      setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet);
759    } else if (ts.isExportAssignment(statement) || ts.isExportDeclaration(statement)) {
760      setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet);
761    }
762  });
763
764  return isCopyrightDeleted;
765}
766
767function setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet) {
768  if (ts.isExportAssignment(statement) && deleteSystemApiSet.has(statement.expression.escapedText.toString())) {
769    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
770    needDeleteExport.default = statement.expression.escapedText.toString();
771  } else if (ts.isExportDeclaration(statement)) {
772    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
773    statement.exportClause.elements.forEach((element) => {
774      const exportName = element.propertyName ?
775        element.propertyName.escapedText.toString() :
776        element.name.escapedText.toString();
777      if (deleteSystemApiSet.has(exportName)) {
778        needDeleteExport.exportName.add(element.name.escapedText.toString());
779      }
780    });
781  }
782  //export namespace test {}
783  const modifiers = statement.modifiers;
784  if (modifiers === undefined) {
785    return;
786  }
787  const exportFlag = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
788  const defaultFlag = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword);
789  if (exportFlag && defaultFlag) {
790    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
791    needDeleteExport.default = statement.name.escapedText.toString();
792  } else if (exportFlag) {
793    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
794    needDeleteExport.exportName.add(statement.name.escapedText.toString());
795  }
796}
797
798/**
799 * 获取export节点的名字,只获取第一个关键词
800 * @param {ts.node} statement
801 * @returns {Array<string>}
802 */
803function getExportIdentifierName(statement) {
804  const names = [];
805  if (ts.isExpressionStatement(statement)) {
806    //exports.name = test;
807    if (ts.isBinaryExpression(statement.expression)) {
808      names.push(statement.expression.right.escapedText.toString());
809    }
810  } else if (ts.isExportAssignment(statement)) {
811    //export default test1
812    names.push(statement.expression.escapedText.toString());
813  } else if (ts.isExportDeclaration(statement)) {
814    //export {test1} 、export {test1 as test} 、export * from './featureability'
815    const exportClause = statement.exportClause;
816    if (exportClause) {
817      const specifiers = exportClause.elements;
818      specifiers.forEach((specifier) => {
819        if (ts.isExportSpecifier(specifier)) {
820          const name = specifier.propertyName ? specifier.propertyName : specifier.name;
821          names.push(name.escapedText.toString());
822        }
823      });
824    }
825  }
826  return names;
827}
828
829/**
830 * 遍历处理tsnode节点
831 * @param  context 解析过后的内容
832 * @param  node 解析过后的节点
833 * @returns ts.node
834 */
835function processVisitEachChild(context, node) {
836  return ts.visitEachChild(node, processAllNodes, context); // 遍历所有子节点
837  function processAllNodes(node) {
838    if (ts.isInterfaceDeclaration(node)) {
839      const newMembers = [];
840      node.members.forEach((member) => {
841        if (!isSystemapi(member)) {
842          newMembers.push(member);
843        }
844      });
845      node = ts.factory.updateInterfaceDeclaration(
846        node,
847        node.decorators,
848        node.modifiers,
849        node.name,
850        node.typeParameters,
851        node.heritageClauses,
852        newMembers
853      );
854    } else if (ts.isClassDeclaration(node)) {
855      const newMembers = [];
856      node.members.forEach((member) => {
857        if (!isSystemapi(member)) {
858          newMembers.push(member);
859        }
860      });
861      node = ts.factory.updateClassDeclaration(
862        node,
863        node.decorators,
864        node.modifiers,
865        node.name,
866        node.typeParameters,
867        node.heritageClauses,
868        newMembers
869      );
870    } else if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) {
871      const newStatements = [];
872      node.body.statements.forEach((statement) => {
873        if (!isSystemapi(statement)) {
874          newStatements.push(statement);
875        }
876      });
877      const newModuleBody = ts.factory.updateModuleBlock(node.body, newStatements);
878      node = ts.factory.updateModuleDeclaration(node, node.decorators, node.modifiers, node.name, newModuleBody);
879    } else if (ts.isEnumDeclaration(node)) {
880      const newMembers = [];
881      node.members.forEach((member) => {
882        if (!isSystemapi(member)) {
883          newMembers.push(member);
884        }
885      });
886      node = ts.factory.updateEnumDeclaration(node, node.decorators, node.modifiers, node.name, newMembers);
887    } else if (ts.isStructDeclaration(node)) {
888      const newMembers = [];
889      node.members.forEach((member, index) => {
890        if (index >= 1 && !isSystemapi(member)) {
891          newMembers.push(member);
892        }
893      });
894      node = ts.factory.updateStructDeclaration(
895        node,
896        node.decorators,
897        node.modifiers,
898        node.name,
899        node.typeParameters,
900        node.heritageClauses,
901        newMembers
902      );
903    }
904    return ts.visitEachChild(node, processAllNodes, context);
905  }
906}
907
908/**
909 * 解析reference
910 * @param {string} url reference文件地址
911 */
912function resolveReferences(url) {
913  const obj = referencesMap.get(url);
914  let references = obj.references;
915  if (!references || references.length === 0) {
916    return;
917  }
918  for (let index = 0; index < references.length; index++) {
919    const element = references[index];
920    element.match(PATT.GET_REFERENCEURL);
921    let referencePath = RegExp.$2;
922    referencePath = referencesToOthers(referencePath, REFERENCE_TYPE.TOLOCAL);
923    let fullReferencePath = path.resolve(path.dirname(url), referencePath);
924    if (fs.existsSync(fullReferencePath) && !referencesModuleMap.has(fullReferencePath)) {
925      const content = fs.readFileSync(fullReferencePath, 'utf-8'); //文件内容
926      const fileName = processFileName(fullReferencePath);
927      ts.transpileModule(content, {
928        compilerOptions: {
929          target: ts.ScriptTarget.ES2017,
930        },
931        fileName: fileName,
932        transformers: { before: [resolveCallback(fullReferencePath)] },
933      });
934    }
935  }
936}
937function resolveCallback(url) {
938  return (context) => {
939    const allReferencesIdentifierSet = new Set([]);
940    let allModule = {};
941    return (node) => {
942      const referenceSourceFile = node;
943      node.statements.forEach((statement) => {
944        if (
945          ts.isModuleDeclaration(statement) &&
946          statement.name &&
947          ts.isStringLiteral(statement.name) &&
948          statement.body &&
949          ts.isModuleBlock(statement.body) &&
950          !isSystemapi(statement)
951        ) {
952          ts.visitEachChild(statement, collectAllIdentifier, context);
953          dealExternalStatements(referenceSourceFile);
954          allModule[statement.name.text.toString()] = [...allReferencesIdentifierSet];
955          allReferencesIdentifierSet.clear();
956        }
957      });
958      referencesModuleMap.set(url, allModule);
959      allModule = {};
960      return node;
961    };
962    function dealExternalStatements(node) {
963      node.statements.forEach((statement) => {
964        let name = '';
965        if (isSystemapi(statement)) {
966          if (ts.isVariableStatement(statement)) {
967            name = variableStatementGetEscapedText(statement);
968          } else if (
969            ts.isInterfaceDeclaration(statement) ||
970            ts.isClassDeclaration(statement) ||
971            ts.isEnumDeclaration(statement)
972          ) {
973            if (statement && statement.name && statement.name.escapedText) {
974              name = statement.name.escapedText.toString();
975            }
976          }
977          allReferencesIdentifierSet.delete(name);
978        }
979      });
980    }
981    function collectAllIdentifier(node) {
982      if (isSystemapi(node)) {
983        return node;
984      }
985      if (ts.isIdentifier(node)) {
986        allReferencesIdentifierSet.add(node.escapedText.toString());
987      }
988      return ts.visitEachChild(node, collectAllIdentifier, context);
989    }
990  };
991}
992
993function variableStatementGetEscapedText(statement) {
994  let name = '';
995  if (
996    statement &&
997    statement.declarationList &&
998    statement.declarationList.declarations &&
999    statement.declarationList.declarations.length > 0 &&
1000    statement.declarationList.declarations[0].name &&
1001    statement.declarationList.declarations[0].name.escapedText
1002  ) {
1003    name = statement.declarationList.declarations[0].name.escapedText.toString();
1004  }
1005  return name;
1006}
1007
1008function isSystemapi(node) {
1009  const notesContent = node.getFullText().replace(node.getText(), '').replace(/[\s]/g, '');
1010  const notesArr = notesContent.split(/\/\*\*/);
1011  const notesStr = notesArr[notesArr.length - 1];
1012  if (notesStr.length !== 0) {
1013    return /@systemapi/g.test(notesStr);
1014  }
1015  return false;
1016}
1017
1018function isEmptyFile(node) {
1019  let isEmpty = true;
1020  if (ts.isSourceFile(node) && node.statements) {
1021    for (let i = 0; i < node.statements.length; i++) {
1022      const statement = node.statements[i];
1023      if (ts.isImportDeclaration(statement)) {
1024        continue;
1025      }
1026      isEmpty = false;
1027      break;
1028    }
1029  }
1030  return isEmpty;
1031}
1032
1033const apiSourcePath = '../api';
1034const outputPath = path.resolve(__dirname, 'output');
1035collectDeclaration(apiSourcePath); //入口
1036