• 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');
18const commander = require('commander');
19
20let sourceFile = null;
21let lastNoteStr = '';
22let lastNodeName = '';
23let etsType = 'ets';
24let componentEtsFiles = [];
25let componentEtsDeleteFiles = ['plugincomponent', 'uiextensioncomponent', 'effectcomponent', 'inspector'];
26const referencesMap = new Map();
27const referencesModuleMap = new Map();
28const kitFileNeedDeleteMap = new Map();
29const ARKTS_FLAG = 'use static';
30const COMPILER_OPTIONS = {
31  target: ts.ScriptTarget.ES2017,
32  etsAnnotationsEnable: true
33};
34/**
35 * @enum {string} references地址的切换类型
36 */
37const REFERENCE_TYPE = {
38  TOLOCAL: 'toLocal',
39  TORIGHTSDK: 'toRightSDK',
40  TOSDK: 'toSDK',
41};
42const PATT = {
43  GET_REFERENCE: /\/\/\/\s*<reference.*>/g,
44  GET_REFERENCEURL: /\/\/\/\s*<reference\s*path=("|')(.*)("|')\s*\/>/g,
45  REFERENCEURL_LOCAL: /(.\/)?(\S*)@internal\/component\/ets\/(\S*)/g,
46  REFERENCEURL_RIGHTSDK: /(..\/)(\S*)build-tools\/ets-loader\/declarations\/(\S*)/g,
47  REFERENCEURL_SDK: /(..\/)(\S*)component\/(\S*)/g,
48};
49const METHOD_KIND = [
50  ts.SyntaxKind.MethodDeclaration,
51  ts.SyntaxKind.FunctionDeclaration,
52  ts.SyntaxKind.MethodSignature,
53  ts.SyntaxKind.Constructor
54];
55
56function start() {
57  const program = new commander.Command();
58  program
59    .name('deleteSystemApi')
60    .version('0.0.1');
61  program
62    .option('--input <string>', 'path name')
63    .option('--output <string>', 'output path')
64    .option('--type <string>', 'ets type')
65    .action((opts) => {
66      outputPath = opts.output;
67      inputDir = opts.input;
68      etsType = opts.type;
69      collectDeclaration(opts.input);
70    });
71  program.parse(process.argv);
72}
73
74function collectDeclaration(inputDir) {
75  // 入口
76  try {
77    const arktsPath = path.resolve(inputDir, '../arkts');
78    const kitPath = path.resolve(inputDir, '../kits');
79    const utFiles = [];
80    collectComponentEtsFiles();
81    readFile(inputDir, utFiles); // 读取文件
82    readFile(arktsPath, utFiles); // 读取文件
83    tsTransform(utFiles, deleteSystemApi);
84    tsTransformKitFile(kitPath);
85  } catch (error) {
86    console.error('DELETE_SYSTEM_PLUGIN ERROR: ', error);
87  }
88}
89
90function collectComponentEtsFiles() {
91  const ComponentDir = path.resolve(inputDir, '@internal', 'component', 'ets');
92  readFile(ComponentDir, componentEtsFiles); // 读取文件
93  const arkuiComponentDir = path.resolve(inputDir, 'arkui', 'component');
94  readFile(arkuiComponentDir, componentEtsFiles); // 读取文件
95  componentEtsFiles = componentEtsFiles.map(item => {
96    return getPureName(item);
97  });
98}
99
100function getPureName(name) {
101  return path
102    .basename(name)
103    .replace('.static.ets', '')
104    .replace('.static.d.ets', '')
105    .replace('.d.ts', '')
106    .replace('.d.ets', '')
107    .replace(/_/g, '')
108    .toLowerCase();
109}
110
111/**
112 * 解析url目录下方的kit文件,删除对应systemapi
113 * @param { string } kitPath kit文件路径
114 */
115function tsTransformKitFile(kitPath) {
116  kitFileNeedDeleteMap.delete('');
117  if (kitFileNeedDeleteMap.length === 0) {
118    return;
119  }
120  const kitFiles = [];
121  readFile(kitPath, kitFiles); // 读取文件
122  kitFiles.forEach((kitFile) => {
123    const kitName = processFileNameWithoutExt(kitFile).replace('@kit.', '');
124    const content = fs.readFileSync(kitFile, 'utf-8');
125    const fileName = processFileName(kitFile);
126    let sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES2017, true);
127    const sourceInfo = getKitNewSourceFile(sourceFile, kitName);
128    if (isEmptyFile(sourceInfo.sourceFile)) {
129      return;
130    }
131    const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
132    let result = printer.printNode(ts.EmitHint.Unspecified, sourceInfo.sourceFile, sourceFile);
133    if (sourceInfo.copyrightMessage !== '') {
134      result = sourceInfo.copyrightMessage + result;
135    }
136    writeFile(kitFile, result);
137  });
138}
139
140/**
141 * 处理kit中需要删除的节点,在其他文件被systemapi修饰的api
142 * @param { ts.SourceFile } sourceFile
143 * @param { string } kitName
144 * @returns 删除完的节点,全部删除为空字符串
145 */
146function getKitNewSourceFile(sourceFile, kitName) {
147  const newStatements = [];
148  const needDeleteExportName = new Set();
149  let copyrightMessage = '';
150  // 初始化ts工厂
151  const factory = ts.factory;
152  sourceFile.statements.forEach((statement, index) => {
153    if (ts.isImportDeclaration(statement)) {
154      const newStatement = processKitImportDeclaration(statement, needDeleteExportName);
155      if (newStatement) {
156        newStatements.push(newStatement);
157      } else if (index === 0) {
158        copyrightMessage = sourceFile.getFullText().replace(sourceFile.getText(), '');
159      }
160    } else if (ts.isExportDeclaration(statement)) {
161      const exportSpecifiers = statement.exportClause?.elements?.filter((item) => {
162        return !needDeleteExportName.has(item.name.escapedText.toString());
163      });
164      if (exportSpecifiers && exportSpecifiers.length !== 0) {
165        statement.exportClause = factory.updateNamedExports(statement.exportClause, exportSpecifiers);
166        newStatements.push(statement);
167      }
168    }
169  });
170  sourceFile = factory.updateSourceFile(sourceFile, newStatements);
171  return { sourceFile, copyrightMessage };
172}
173
174function addImportToNeedDeleteExportName(importClause, needDeleteExportName) {
175  if (importClause.name) {
176    needDeleteExportName.add(importClause.name.escapedText.toString());
177  }
178  const namedBindings = importClause.namedBindings;
179  if (namedBindings !== undefined && ts.isNamedImports(namedBindings)) {
180    const elements = namedBindings.elements;
181    elements.forEach((element) => {
182      const exportName = element.propertyName ?
183        element.propertyName.escapedText.toString() :
184        element.name.escapedText.toString();
185      needDeleteExportName.add(element.name.escapedText.toString());
186    });
187  }
188}
189/**
190 * 根据节点和需要删除的节点数据生成新节点
191 * @param { ts.ImportDeclaration } statement 需要处理的import节点
192 * @param { Map} needDeleteMap 需要删除的节点数据
193 * @param { Map} needDeleteExportName 需要删除的导出节点
194 * @returns { ts.ImportDeclaration | undefined } 返回新的import节点,全部删除为undefined
195 */
196function processKitImportDeclaration(statement, needDeleteExportName) {
197  // 初始化ts工厂
198  const factory = ts.factory;
199  const importClause = statement.importClause;
200  if (!ts.isImportClause(importClause)) {
201    return statement;
202  }
203  const importPath = statement.moduleSpecifier.text.replace('../', '');
204  if (kitFileNeedDeleteMap === undefined || !kitFileNeedDeleteMap.has(importPath)) {
205    const hasFilePath = hasFileByImportPath(inputDir, importPath);
206    if (hasFilePath) {
207      return statement;
208    }
209    addImportToNeedDeleteExportName(importClause, needDeleteExportName);
210    return undefined;
211  }
212  const currImportInfo = kitFileNeedDeleteMap.get(importPath);
213  let defaultName = '';
214  let importNodeNamedBindings = [];
215  if (importClause.name) {
216    if (currImportInfo.default === importClause.name.escapedText.toString()) {
217      //import buffer from "../@ohos.buffer";
218      needDeleteExportName.add(currImportInfo.default);
219    } else {
220      defaultName = importClause.name.escapedText.toString();
221    }
222  }
223  const namedBindings = importClause.namedBindings;
224  if (namedBindings !== undefined && ts.isNamedImports(namedBindings)) {
225    const elements = namedBindings.elements;
226    elements.forEach((element) => {
227      const exportName = element.propertyName ?
228        element.propertyName.escapedText.toString() :
229        element.name.escapedText.toString();
230      if (!currImportInfo.exportName.has(exportName)) {
231        importNodeNamedBindings.push(factory.createImportSpecifier(false, element.propertyName, element.name));
232      } else {
233        needDeleteExportName.add(element.name.escapedText.toString());
234      }
235    });
236  }
237  if (defaultName !== '' || importNodeNamedBindings.length !== 0) {
238    const newImportNode = factory.createImportDeclaration(
239      undefined,
240      factory.createImportClause(
241        false,
242        defaultName === '' ? undefined : factory.createIdentifier(defaultName),
243        importNodeNamedBindings.length === 0 ? undefined : factory.createNamedImports(importNodeNamedBindings)
244      ),
245      statement.moduleSpecifier
246    );
247    return newImportNode;
248  }
249  return undefined;
250}
251
252/**
253 * 判断文件路径对应的文件是否存在
254 * @param {string} apiDir 引用接口所在目录
255 * @param {string} importPath kit文件import
256 * @returns {boolean} importPath是否存在
257 */
258function hasFileByImportPath(apiDir, importPath) {
259  let fileDir = path.resolve(apiDir);
260  if (importPath.startsWith('@arkts')) {
261    fileDir = path.resolve(inputDir, '../arkts');
262  }
263  return isExistImportFile(fileDir, importPath) ||
264    isExistArkUIFile(path.resolve(inputDir, 'arkui', 'component'), importPath);
265}
266
267
268/**
269 * Arkui import路径特殊处理
270 * @param {string} resolvedPath 引用接口所在目录
271 * @param {string} importPath kit文件import
272 * @returns {boolean} importPath是否存在
273 */
274function isExistArkUIFile(resolvedPath, importPath) {
275  // TODO arkui 特殊处理import
276  if (importPath.startsWith('@ohos.arkui.')) {
277    resolvedPath = path.resolve(inputDir);
278  }
279  if (importPath.startsWith('arkui.component.') || importPath.startsWith('arkui.stateManagement.')) {
280    resolvedPath = path.resolve(inputDir);
281    importPath = importPath.replace(/\./g, '/');
282  }
283  const filePath = path.resolve(resolvedPath, importPath);
284  if (
285    filePath.includes(path.resolve(inputDir, '@internal', 'component', 'ets')) ||
286    filePath.includes(path.resolve(inputDir, 'arkui', 'component'))
287  ) {
288    const fileName = getPureName(filePath);
289    return componentEtsFiles.includes(fileName);
290  }
291  return isExistImportFile(resolvedPath, importPath);
292}
293
294function isExistImportFile(fileDir, importPath) {
295  return ['.d.ts', '.d.ets', '.static.d.ets'].some(ext => {
296    return fs.existsSync(path.resolve(fileDir, `${importPath}${ext}`));
297  });
298}
299
300/**
301 * 统一处理文件名称,修改后缀等
302 * @param {string} filePath 文件路径
303 * @returns {string} filename 文件名称
304 */
305function processFileName(filePath) {
306  return path
307    .basename(filePath)
308    .replace(/\.d\.ts$/g, '.ts')
309    .replace(/\.d\.ets$/g, '.ets');
310}
311
312function processFileNameWithoutExt(filePath) {
313  return path
314    .basename(filePath)
315    .replace(/\.d\.ts$/g, '')
316    .replace(/\.d\.ets$/g, '')
317    .replace(/\.ts$/g, '')
318    .replace(/\.ets$/g, '');
319}
320
321/**
322 * 遍历所有文件进行处理
323 * @param {Array} utFiles 所有文件
324 * @param {deleteSystemApi} callback 回调函数
325 */
326function tsTransform(utFiles, callback) {
327  utFiles.forEach((url) => {
328    const apiBaseName = path.basename(url);
329    let content = fs.readFileSync(url, 'utf-8'); // 文件内容
330    let isTransformer = /\.d\.ts/.test(apiBaseName) || /\.d\.ets/.test(apiBaseName);
331    if (/\.json/.test(url) || apiBaseName === 'index-full.d.ts') {
332      isTransformer = false;
333    }
334    if (etsType === 'ets2') {
335      if (!/\@systemapi/.test(content) && apiBaseName !== '@ohos.arkui.component.d.ets') {
336        isTransformer = false;
337      }
338    }
339
340    if (!isTransformer) {
341      writeFile(url, content);
342      return;
343    }
344    // dts文件处理
345    const fileName = processFileName(url);
346    let references = content.match(PATT.GET_REFERENCE);
347    if (references) {
348      referencesMap.set(url, { references: references });
349      for (let index = 0; index < references.length; index++) {
350        const item = references[index];
351        content = content.replace(item, '');
352      }
353    }
354    ts.transpileModule(content, {
355      compilerOptions: COMPILER_OPTIONS,
356      fileName: fileName,
357      transformers: { before: [callback(url)] },
358    });
359  });
360}
361
362/**
363 * 切换references或者references中path的格式
364 * @param {string} references references或者references中的path
365 * @param {REFERENCE_TYPE} reverse 切换类型
366 * @returns {string}
367 */
368function referencesToOthers(references, type) {
369  let referencesurl;
370  let hasFullpatt = references.match(PATT.GET_REFERENCEURL);
371  let isFullReferenceurl = hasFullpatt && hasFullpatt.length > 0;
372  if (isFullReferenceurl) {
373    referencesurl = RegExp.$2;
374  } else {
375    referencesurl = references;
376  }
377  let currentType = '';
378  if (referencesurl.match(PATT.REFERENCEURL_LOCAL)) {
379    currentType = REFERENCE_TYPE.TOLOCAL;
380  } else if (referencesurl.match(PATT.REFERENCEURL_RIGHTSDK)) {
381    currentType = REFERENCE_TYPE.TORIGHTSDK;
382  } else if (referencesurl.match(PATT.REFERENCEURL_SDK)) {
383    currentType = REFERENCE_TYPE.TOSDK;
384  }
385  if (currentType === '' || currentType === type) {
386    return references;
387  }
388  let starturl = '';
389  let fileName = '';
390  switch (currentType) {
391    case REFERENCE_TYPE.TOLOCAL:
392      starturl = RegExp.$2;
393      fileName = RegExp.$3;
394      break;
395    case REFERENCE_TYPE.TORIGHTSDK:
396      starturl = RegExp.$2;
397      fileName = RegExp.$3;
398      break;
399    case REFERENCE_TYPE.TOSDK:
400      starturl = RegExp.$2;
401      fileName = RegExp.$3;
402      break;
403    default:
404      break;
405  }
406  let finallyurl;
407  switch (type) {
408    case REFERENCE_TYPE.TOLOCAL:
409      finallyurl = `${starturl === '' ? './' : ''}${starturl}@internal/component/ets/${fileName}`;
410      break;
411    case REFERENCE_TYPE.TORIGHTSDK:
412      finallyurl = `../${starturl}build-tools/ets-loader/declarations/${fileName}`;
413      break;
414    case REFERENCE_TYPE.TOSDK:
415      finallyurl = `../${starturl}component/${fileName}`;
416      break;
417    default:
418      break;
419  }
420  if (isFullReferenceurl) {
421    finallyurl = `/// <reference path="${finallyurl}"/>`;
422  }
423  return finallyurl;
424}
425
426/**
427 * 读取目录下所有文件
428 * @param {string} dir 文件目录
429 * @param {Array} utFiles 所有文件
430 */
431function readFile(dir, utFiles) {
432  try {
433    const files = fs.readdirSync(dir);
434    files.forEach((element) => {
435      const filePath = path.join(dir, element);
436      const status = fs.statSync(filePath);
437      if (status.isDirectory()) {
438        readFile(filePath, utFiles);
439      } else {
440        utFiles.push(filePath);
441      }
442    });
443  } catch (e) {
444    console.log('ETS ERROR: ' + e);
445  }
446}
447
448function writeFile(url, data, option) {
449  const urlPath = url.replace(/\.static\.d\.ets$/, '.d.ets');
450  const urlDirName = path.dirname(inputDir);
451  const relativePath = path.relative(urlDirName, urlPath);
452  const newFilePath = path.resolve(outputPath, relativePath);
453  fs.mkdir(path.dirname(newFilePath), { recursive: true }, (err) => {
454    if (err) {
455      console.log(`ERROR FOR CREATE PATH ${err}`);
456    } else {
457      if (data === '') {
458        fs.rmSync(newFilePath, { force: true });
459        return;
460      }
461      fs.writeFileSync(newFilePath, data, option, (err) => {
462        if (err) {
463          console.log(`ERROR FOR CREATE FILE ${err}`);
464        }
465      });
466    }
467  });
468}
469
470const globalModules = new Map();
471
472/**
473 * 遍历处理overload节点
474 * @param  context 解析过后的内容
475 * @param  node 解析过后的节点
476 * @returns ts.node
477 */
478function visitEachChild(context, node) {
479  return ts.visitEachChild(node, processAllNodes, context); // 遍历所有子节点
480  function processAllNodes(node) {
481    if (ts.isOverloadDeclaration(node)) {
482      node = processInterfaceDeclaration(node);
483    }
484    return ts.visitEachChild(node, processAllNodes, context);
485  }
486  function processInterfaceDeclaration(overloadNode) {
487    // 获取方法类型兄弟节点列表
488    const parentNode = overloadNode.parent;
489    const brotherNodes = [];
490    const brotherFuntionNames = new Set([]);
491    if (ts.isSourceFile(parentNode) || ts.isModuleBlock(parentNode)) {
492      brotherNodes.push(...parentNode.statements);
493    } else if (ts.isInterfaceDeclaration(parentNode) || ts.isClassDeclaration(parentNode)) {
494      brotherNodes.push(...parentNode.members);
495    }
496    if (brotherNodes.length === 0) {
497      return undefined;
498    }
499    brotherNodes.forEach(brotherNode => {
500      if (METHOD_KIND.includes(brotherNode.kind) && brotherNode.name && ts.isIdentifier(brotherNode.name) &&
501        !brotherFuntionNames.has(brotherNode.name.escapedText.toString())) {
502        brotherFuntionNames.add(brotherNode.name.escapedText.toString());
503      }
504    });
505
506    // 更新overload节点
507    const overloadChildren = overloadNode.members;
508    if (overloadChildren.length === 0) {
509      return undefined;
510    }
511    const newChildren = [];
512    overloadChildren.forEach(overloadChild => {
513      if (overloadChild.name && ts.isIdentifier(overloadChild.name) &&
514        brotherFuntionNames.has(overloadChild.name.escapedText.toString())) {
515        newChildren.push(overloadChild);
516      }
517    });
518    if (newChildren.length === 0) {
519      return undefined;
520    }
521    return ts.factory.updateOverloadDeclaration(overloadNode, overloadNode.modifier, overloadNode.name, newChildren);
522  }
523}
524
525/**
526 * 每个文件处理前回调函数第二个
527 * @param {string} url 文件路径
528 * @param {string} copyrightMessage 版权头注释
529 * @param {string} fileAndKitComment 文件kit注释
530 * @param {boolean} firstNodeIsStatic 第一个节点是否为use static
531 * @returns {Function}
532 */
533function formatImportDeclaration(url, copyrightMessage = '', fileAndKitComment = '', firstNodeIsStatic = false) {
534  const allIdentifierSet = new Set([]);
535  return (context) => {
536    return (node) => {
537      sourceFile = node;
538      collectAllIdentifier(context, node, allIdentifierSet); // 获取所有标识符
539      formatValue = formatAllNodes(url, node, allIdentifierSet); // 获取所有节点
540      node = visitEachChild(context, formatValue.node);
541      const referencesMessage = formatValue.referencesMessage;
542      if (isEmptyFile(node)) {
543        return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None);
544      }
545      const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
546      let result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
547      result = collectCopyrightMessage(result, copyrightMessage, fileAndKitComment, firstNodeIsStatic);
548      copyrightMessage = node.getFullText().replace(node.getText(), '');
549      if (referencesMessage) { // 将references写入文件
550        result = result.substring(0, copyrightMessage.length) + '\n' + referencesMessage +
551          result.substring(copyrightMessage.length);
552      }
553      result = removeSystemapiDoc(result);
554      writeFile(url, result);
555      return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None);
556    };
557  };
558}
559
560function collectAllIdentifier(context, node, allIdentifierSet) {
561  if (!ts.isSourceFile(node) || !node.statements) {
562    return;
563  }
564  node.statements.forEach((stat) => {
565    if (stat.illegalDecorators) {
566      collectillegalNodes(ts.getAllDecorators(stat), allIdentifierSet);
567    }
568    if (!ts.isImportDeclaration(stat)) {
569      ts.visitEachChild(stat, collectAllNodes(context, allIdentifierSet), context);
570    }
571  });
572}
573
574function collectAllNodes(context, allIdentifierSet) {
575  return (node) => {
576    if (ts.isIdentifier(node)) {
577      allIdentifierSet.add(node.escapedText.toString());
578    }
579    return ts.visitEachChild(node, collectAllNodes(context, allIdentifierSet), context);
580  };
581}
582
583function collectillegalNodes(decorator, allIdentifierSet) {
584  decorator.forEach(decorator => {
585    allIdentifierSet.add(decorator.expression.escapedText.toString());
586  });
587}
588
589/**
590 * 给result添加被systemapi裁剪掉的版权头信息和kit标签
591 * @param {string} result 文件内容
592 * @param {string} copyrightMessage 版权头注释
593 * @param {string} fileAndKitComment 文件kit注释
594 * @param {boolean} firstNodeIsStatic 第一个节点是否为use static
595 * @returns
596 */
597function collectCopyrightMessage(result, copyrightMessage, fileAndKitComment, firstNodeIsStatic) {
598  const newFileAndKitComment = getFileAndKitComment(result);
599  const newCopyrightMessage = getCopyrightComment(result);
600  let commentIndex = 0;
601  if (firstNodeIsStatic) {
602    const indexStatic = result.match(/use static.*\n/);
603    if (indexStatic) {
604      commentIndex = indexStatic.index + indexStatic[0].length;
605    }
606  }
607  if (newFileAndKitComment === '') {
608    result =
609      result.substring(0, commentIndex) +
610      fileAndKitComment +
611      '\n\n' +
612      result.substring(commentIndex);
613  }
614  if (newCopyrightMessage === '') {
615    // 多段注释中间需要换行
616    result =
617      result.substring(0, commentIndex) +
618      copyrightMessage +
619      '\n\n' +
620      result.substring(commentIndex);
621  }
622  return result;
623}
624
625function formatAllNodes(url, node, allIdentifierSet) {
626  let referencesMessage = '';
627  let currReferencesModule = formatAllNodesReferences(url);
628  if (!ts.isSourceFile(node) || !node.statements) {
629    return { node, referencesMessage };
630  }
631  const newStatements = [];
632  node.statements.forEach((statement) => {
633    if (ts.isImportDeclaration(statement)) {
634      const importInfo = formatAllNodesImportDeclaration(
635        node,
636        statement,
637        url,
638        currReferencesModule,
639        allIdentifierSet
640      );
641      if (importInfo) {
642        newStatements.push(statement);
643      }
644    } else if (ts.isStructDeclaration(statement)) {
645      statement = ts.factory.updateStructDeclaration(
646        statement,
647        statement.modifiers,
648        statement.name,
649        statement.typeParameters,
650        statement.heritageClauses,
651        statement.members.slice(1)
652      );
653      newStatements.push(statement);
654    } else {
655      newStatements.push(statement);
656    }
657  });
658  currReferencesModule.forEach((item) => {
659    if (item.isUsed) {
660      referencesMessage += item.reference + '\n';
661    }
662  });
663  node = ts.factory.updateSourceFile(node, newStatements);
664  return { node, referencesMessage };
665}
666
667function hasCopyright(node) {
668  return /http\:\/\/www\.apache\.org\/licenses\/LICENSE\-2\.0/g.test(node.getFullText().replace(node.getText(), ''));
669}
670
671/**
672 * 处理References节点
673 * @param {ts.node} node 当前节点
674 * @param {string} url 文件路径
675 * @returns {Array} currReferencesModule 返回该文件的references数据
676 */
677function formatAllNodesReferences(url) {
678  globalModules.clear();
679  let currReferences = [];
680  let currReferencesModule = [];
681  if (referencesMap.has(url)) {
682    currReferences = referencesMap.get(url);
683    currReferencesModule = currReferences.references.map((element, index) => {
684      element.match(PATT.GET_REFERENCEURL);
685      let referencePath = RegExp.$2;
686      referencePath = referencesToOthers(referencePath, REFERENCE_TYPE.TOLOCAL);
687      let fullReferencePath = path.resolve(path.dirname(url), referencePath);
688      let module = referencesModuleMap.get(fullReferencePath);
689      for (const key in module) {
690        if (Object.hasOwnProperty.call(module, key)) {
691          globalModules.set(key, index);
692        }
693      }
694      return { modules: module, fullReferencePath: fullReferencePath, reference: element, isUsed: false };
695    });
696  }
697  return currReferencesModule;
698}
699
700/**
701 * 处理Import节点 去除未使用、不存在、References中没有对应模块的导入
702 * @param {ts.node} node 当前节点
703 * @param {ts.ImportDeclaration} statement 导入节点
704 * @param {string} url 文件路径
705 * @param {Array} currReferencesModule 该文件的所有Identifier关键字
706 * @param {Set<string>} allIdentifierSet 该文件的所有Identifier关键字
707 * @returns {ts.ImportDeclaration|undefined} statement 处理完成的导入节点、copyrightMessage
708 */
709function formatAllNodesImportDeclaration(node, statement, url, currReferencesModule, allIdentifierSet) {
710  const clauseSet = collectClauseSet(statement);
711  const importSpecifier = statement.moduleSpecifier.getText().replace(/[\'\"]/g, '');
712  const fileDir = path.dirname(url);
713  let hasImportSpecifierFile = hasFileByImportPath(fileDir, importSpecifier);
714  let hasImportSpecifierInModules = globalModules.has(importSpecifier);
715  if (!hasImportSpecifierFile && !hasImportSpecifierInModules) {
716    //路径不存在 或者 无reference使用
717    return undefined;
718  }
719  let currModule = [];
720  if (hasImportSpecifierInModules) {
721    let index = globalModules.get(importSpecifier);
722    currModule = currReferencesModule[index].modules[importSpecifier];
723  }
724  let { exsitClauseSet, hasExsitStatus, hasNonExsitStatus } =
725    collectNeedDeleteFlag(clauseSet, allIdentifierSet, hasImportSpecifierInModules, currModule);
726  if (!hasExsitStatus) {
727    // 不存在需要使用的标识符
728    return undefined;
729  }
730  if (hasImportSpecifierInModules) {
731    let index = globalModules.get(importSpecifier);
732    currReferencesModule[index].isUsed = true;
733  }
734  if (!hasNonExsitStatus) {
735    // 不存在需要删除的标识符
736    return statement;
737  }
738  // 有需要删除的标识符
739  return updataImportNode(statement, exsitClauseSet);
740}
741
742/**
743 * 保留import节点中exsitClauseSet使用到的模块
744 *
745 * @param {ts.ImportDeclaration} statement
746 * @param {Set<string>} exsitClauseSet
747 * @returns
748 */
749function updataImportNode(statement, exsitClauseSet) {
750  const newSpecifiers = [];
751  statement.importClause.namedBindings.elements.forEach((element) => {
752    if (exsitClauseSet.has(element.name.escapedText.toString())) {
753      newSpecifiers.push(element);
754    }
755  });
756  statement.importClause.namedBindings = ts.factory.updateNamedImports(
757    statement.importClause.namedBindings,
758    newSpecifiers
759  );
760  return statement;
761}
762
763/**
764 * 判断当前import的模块是否需要删除
765 *
766 * @param {Set<string>} clauseSet 当前模块集合
767 * @param {Set<string>} allIdentifierSet 当前文件使用到的Identifer名称
768 * @param {boolean} hasImportSpecifierInModules reference特殊路径是否存在
769 * @param {Array<any>} currModule reference模块信息
770 * @returns
771 */
772function collectNeedDeleteFlag(clauseSet, allIdentifierSet, hasImportSpecifierInModules, currModule) {
773  let exsitClauseSet = new Set([]); // 当前import使用到的模块
774  let hasExsitStatus = false; // 存在需要使用的模块
775  let hasNonExsitStatus = false; //存在不需要使用的模块
776  for (const clause of clauseSet) {
777    let flag = allIdentifierSet.has(clause);
778    if (hasImportSpecifierInModules) {
779      flag = allIdentifierSet.has(clause) && currModule.includes(clause);
780    }
781    if (flag) {
782      // 标识符使用到了当前import中的引用
783      exsitClauseSet.add(clause);
784      hasExsitStatus = true;
785    } else {
786      hasNonExsitStatus = true;
787    }
788  }
789  return {
790    exsitClauseSet,
791    hasExsitStatus,
792    hasNonExsitStatus
793  };
794}
795
796/**
797 * 收集import节点的导入模块
798 *
799 * @param {ts.ImportDeclaration} statement 导入节点
800 * @returns {Set<string>} clauseSet
801 */
802function collectClauseSet(statement) {
803  // 是import节点 import {AsyncCallback} from './@ohos.base';
804  const clauseSet = new Set([]);
805  if (!statement.importClause || !ts.isImportClause(statement.importClause)) {
806    return clauseSet;
807  }
808  // 标识符
809  const clauseNode = statement.importClause;
810  if (!clauseNode.namedBindings && clauseNode.name && ts.isIdentifier(clauseNode.name)) {
811    // 没有大括号的标识符
812    clauseSet.add(clauseNode.name.escapedText.toString());
813  } else if (
814    clauseNode.namedBindings &&
815    clauseNode.namedBindings.name &&
816    ts.isIdentifier(clauseNode.namedBindings.name)
817  ) {
818    // 没有标识符 *号
819    clauseSet.add(clauseNode.namedBindings.name.escapedText.toString());
820  } else if (clauseNode.namedBindings && clauseNode.namedBindings.elements) {
821    // 有花括号的标识符
822    clauseNode.namedBindings.elements.forEach((ele) => {
823      if (ele.name && ts.isIdentifier(ele.name)) {
824        clauseSet.add(ele.name.escapedText.toString());
825      }
826    });
827  }
828  return clauseSet;
829}
830
831/**
832 *
833 * 防止@file和@kit段注释丢失
834 * @param {string} fileFullText
835 * @returns {string}
836 *
837 */
838function getFileAndKitComment(fileFullText) {
839  let fileAndKitComment = '';
840  let pattern = /\/\*\*\s*\*\s*@file[\s\S]*?@kit[\s\S]*?\*\//;
841  let comment = fileFullText.match(pattern);
842  if (comment) {
843    fileAndKitComment = comment[0];
844  }
845  return fileAndKitComment;
846}
847
848/**
849 *
850 * 防止版权头段注释丢失
851 * @param {string} fileFullText
852 * @returns {string}
853 *
854 */
855function getCopyrightComment(fileFullText) {
856  let copyrightComment = '';
857  let pattern = /\/\*\s*\r?\n\s*\*\s*Copyright[\s\S]*?\*\//g;
858  let comment = fileFullText.match(pattern);
859  if (comment) {
860    copyrightComment = comment[0];
861  }
862  return copyrightComment;
863}
864
865/**
866 * 处理最终结果中的systemapi
867 * @param {string} result
868 */
869function removeSystemapiDoc(result) {
870  result.split;
871  return result.replace(/\/\*\*[\s\S]*?\*\//g, (substring, p1) => {
872    return /@systemapi/g.test(substring) ? '' : substring;
873  });
874}
875
876/**
877 * 每个文件处理前回调函数第一个
878 * @callback deleteSystemApi
879 * @param {string} url 文件路径
880 * @returns {Function}
881 */
882function deleteSystemApi(url) {
883  return (context) => {
884    return (node) => {
885      const fullText = String(node.getFullText());
886      //获取文件头部的注释信息--这里可能会涉及到@file和@kit段注释丢失
887      const fileAndKitComment = getFileAndKitComment(fullText);
888      const copyrightMessage = getCopyrightComment(fullText);
889      let kitName = '';
890      if (fullText.match(/\@kit (.*)\r?\n/g)) {
891        kitName = RegExp.$1.replace(/\s/g, '');
892      }
893      sourceFile = node;
894      const deleteNode = processSourceFile(node, kitName, url); // 处理最外层节点
895      node = processVisitEachChild(context, deleteNode.node);
896      if (!isEmptyFile(node)) {
897        const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
898        const result = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
899        if (referencesMap.has(url)) {
900          resolveReferences(url);
901        }
902        const fileName = processFileName(url);
903        ts.transpileModule(result, {
904          compilerOptions: COMPILER_OPTIONS,
905          fileName: fileName,
906          transformers: { before: [formatImportDeclaration(url, copyrightMessage, fileAndKitComment, deleteNode.firstNodeIsStatic)] },
907        });
908      }
909      return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None);
910    };
911  };
912}
913
914exports.deleteSystemApi = deleteSystemApi;
915
916/**
917 * 遍历每个文件下的所有节点,然后删除节点
918 * @param node
919 * @returns
920 */
921
922/**
923 * 处理最外层的节点看是否删除
924 * @param {ts.Node} node 解析过后的节点
925 * @param {string} url 当前文件kitName
926 * @returns
927 */
928function processSourceFile(node, url) {
929  let firstNodeIsStatic = false;
930  const newStatements = [];
931  const newStatementsWithoutExport = [];
932  const deleteSystemApiSet = new Set();
933  let needDeleteExport = {
934    fileName: '',
935    default: '',
936    exportName: new Set(),
937  };
938  firstNodeIsStatic = addNewStatements(node, newStatements, deleteSystemApiSet, needDeleteExport);
939  newStatements.forEach((statement) => {
940    const names = getExportIdentifierName(statement);
941    if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && statement.moduleSpecifier.text.startsWith('./arkui/component/')) {
942      const importPath = statement.moduleSpecifier.text.replace('./arkui/component/', '');
943      const isDeleteSystemFile = componentEtsDeleteFiles.includes(getPureName(importPath));
944      const hasEtsFile = componentEtsFiles.includes(getPureName(importPath));
945      const existFile = isExistImportFile(path.dirname(url), statement.moduleSpecifier.text.toString());
946      if (isDeleteSystemFile || !hasEtsFile && !existFile) {
947        return;
948      }
949    }
950    if (names.length === 0) {
951      newStatementsWithoutExport.push(statement);
952      return;
953    }
954    if (names.length === 1 && !deleteSystemApiSet.has(names[0])) {
955      //exports.name = test;
956      //export default test1
957      //export {test1}
958      newStatementsWithoutExport.push(statement);
959      return;
960    }
961    processExportNode(statement, node, needDeleteExport, names, deleteSystemApiSet, newStatementsWithoutExport);
962  });
963  if (needDeleteExport.fileName !== '') {
964    kitFileNeedDeleteMap.set(needDeleteExport.fileName, needDeleteExport);
965  }
966  return {
967    node: ts.factory.updateSourceFile(node, newStatementsWithoutExport, node.isDeclarationFile, node.referencedFiles),
968    firstNodeIsStatic,
969  };
970}
971
972function processExportNode(statement, node, needDeleteExport, names, deleteSystemApiSet, newStatementsWithoutExport) {
973  //删除export节点信息
974  if (ts.isExportAssignment(statement)) {
975    //export default abilityAccessCtrl;
976    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
977    needDeleteExport.default = statement.expression.escapedText.toString();
978  } else if (ts.isExportDeclaration(statement)) {
979    //export {test1 as test,testa as test2}
980    let needExport = false;
981    const newSpecifiers = [];
982    names.forEach((name, index) => {
983      const exportSpecifier = statement.exportClause.elements[index];
984      if (!deleteSystemApiSet.has(name)) {
985        //未被删除的节点
986        newSpecifiers.push(exportSpecifier);
987        needExport = true;
988      } else {
989        //被删除的节点
990        needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
991        needDeleteExport.exportName.add(exportSpecifier.name.escapedText.toString());
992      }
993    });
994    if (needExport) {
995      statement.exportClause = ts.factory.updateNamedExports(statement.exportClause, newSpecifiers);
996      newStatementsWithoutExport.push(statement);
997    }
998  }
999}
1000
1001function addNewStatements(node, newStatements, deleteSystemApiSet, needDeleteExport) {
1002  let firstNodeIsStatic = false;
1003  node.statements.forEach((statement, index) => {
1004    if (index === 0 && isStaticFlag(statement)) {
1005      firstNodeIsStatic = true;
1006    }
1007    if (!isSystemapi(statement)) {
1008      newStatements.push(statement);
1009      return;
1010    }
1011    if (ts.isVariableStatement(statement)) {
1012      deleteSystemApiSet.add(variableStatementGetEscapedText(statement));
1013    } else if (
1014      ts.isModuleDeclaration(statement) ||
1015      ts.isInterfaceDeclaration(statement) ||
1016      ts.isClassDeclaration(statement) ||
1017      ts.isEnumDeclaration(statement) ||
1018      ts.isStructDeclaration(statement) ||
1019      ts.isTypeAliasDeclaration(statement) ||
1020      ts.isAnnotationDeclaration(statement)
1021    ) {
1022      if (statement && statement.name && statement.name.escapedText) {
1023        deleteSystemApiSet.add(statement.name.escapedText.toString());
1024      }
1025      setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet);
1026    } else if (ts.isExportAssignment(statement) || ts.isExportDeclaration(statement)) {
1027      setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet);
1028    }
1029  });
1030
1031  return firstNodeIsStatic;
1032}
1033
1034function setDeleteExport(statement, node, needDeleteExport, deleteSystemApiSet) {
1035  if (ts.isExportAssignment(statement) && deleteSystemApiSet.has(statement.expression.escapedText.toString())) {
1036    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
1037    needDeleteExport.default = statement.expression.escapedText.toString();
1038  } else if (ts.isExportDeclaration(statement)) {
1039    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
1040    statement.exportClause.elements.forEach((element) => {
1041      const exportName = element.propertyName ?
1042        element.propertyName.escapedText.toString() :
1043        element.name.escapedText.toString();
1044      if (deleteSystemApiSet.has(exportName)) {
1045        needDeleteExport.exportName.add(element.name.escapedText.toString());
1046      }
1047    });
1048  }
1049  //export namespace test {}
1050  const modifiers = statement.modifiers;
1051  if (modifiers === undefined) {
1052    return;
1053  }
1054  const exportFlag = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
1055  const defaultFlag = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword);
1056  if (exportFlag && defaultFlag) {
1057    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
1058    needDeleteExport.default = statement.name.escapedText.toString();
1059  } else if (exportFlag) {
1060    needDeleteExport.fileName = processFileNameWithoutExt(node.fileName);
1061    needDeleteExport.exportName.add(statement.name.escapedText.toString());
1062  }
1063}
1064
1065/**
1066 * 获取export节点的名字,只获取第一个关键词
1067 * @param {ts.node} statement
1068 * @returns {Array<string>}
1069 */
1070function getExportIdentifierName(statement) {
1071  const names = [];
1072  if (ts.isExpressionStatement(statement)) {
1073    //exports.name = test;
1074    if (ts.isBinaryExpression(statement.expression)) {
1075      names.push(statement.expression.right.escapedText.toString());
1076    }
1077  } else if (ts.isExportAssignment(statement)) {
1078    //export default test1
1079    names.push(statement.expression.escapedText.toString());
1080  } else if (ts.isExportDeclaration(statement)) {
1081    //export {test1} 、export {test1 as test} 、export * from './featureability'
1082    const exportClause = statement.exportClause;
1083    if (exportClause) {
1084      const specifiers = exportClause.elements;
1085      specifiers.forEach((specifier) => {
1086        if (ts.isExportSpecifier(specifier)) {
1087          const name = specifier.propertyName ? specifier.propertyName : specifier.name;
1088          names.push(name.escapedText.toString());
1089        }
1090      });
1091    }
1092  }
1093  return names;
1094}
1095
1096/**
1097 * 遍历处理tsnode节点
1098 * @param  context 解析过后的内容
1099 * @param  node 解析过后的节点
1100 * @returns ts.node
1101 */
1102function processVisitEachChild(context, node) {
1103  return ts.visitEachChild(node, processAllNodes, context); // 遍历所有子节点
1104  function processAllNodes(node) {
1105    if (ts.isInterfaceDeclaration(node)) {
1106      node = processInterfaceDeclaration(node);
1107    } else if (ts.isClassDeclaration(node)) {
1108      node = processClassDeclaration(node);
1109    } else if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) {
1110      const newStatements = [];
1111      node.body.statements.forEach((statement) => {
1112        if (!isSystemapi(statement)) {
1113          newStatements.push(statement);
1114        }
1115      });
1116      const newModuleBody = ts.factory.updateModuleBlock(node.body, newStatements);
1117      node = ts.factory.updateModuleDeclaration(
1118        node,
1119        node.modifiers,
1120        node.name,
1121        newModuleBody
1122      );
1123    } else if (ts.isEnumDeclaration(node)) {
1124      node = processEnumDeclaration(node);
1125    } else if (ts.isStructDeclaration(node)) {
1126      node = processStructDeclaration(node);
1127    } else if (ts.isAnnotationDeclaration(node)) {
1128      node = processAnnotationDeclaration(node);
1129    }
1130    return ts.visitEachChild(node, processAllNodes, context);
1131  }
1132}
1133
1134/**
1135 * 处理@interface子节点
1136 */
1137function processAnnotationDeclaration(node) {
1138  const newMembers = [];
1139  node.members.forEach((member) => {
1140    if (!isSystemapi(member)) {
1141      newMembers.push(member);
1142    }
1143  });
1144  node = ts.factory.updateAnnotationDeclaration(
1145    node,
1146    node.modifiers,
1147    node.name,
1148    newMembers
1149  );
1150  return node;
1151}
1152
1153/**
1154 * 处理interface子节点
1155 */
1156function processInterfaceDeclaration(node) {
1157  const newMembers = [];
1158  node.members.forEach((member) => {
1159    if (!isSystemapi(member)) {
1160      newMembers.push(member);
1161    }
1162  });
1163  node = ts.factory.updateInterfaceDeclaration(
1164    node,
1165    node.modifiers,
1166    node.name,
1167    node.typeParameters,
1168    node.heritageClauses,
1169    newMembers
1170  );
1171  return node;
1172}
1173
1174/**
1175 * 处理class子节点
1176 */
1177function processClassDeclaration(node) {
1178  const newMembers = [];
1179  node.members.forEach((member) => {
1180    if (!isSystemapi(member)) {
1181      newMembers.push(member);
1182    }
1183  });
1184  node = ts.factory.updateClassDeclaration(
1185    node,
1186    node.modifiers,
1187    node.name,
1188    node.typeParameters,
1189    node.heritageClauses,
1190    newMembers
1191  );
1192  return node;
1193}
1194
1195/**
1196 * 处理enum子节点
1197 */
1198function processEnumDeclaration(node) {
1199  const newMembers = [];
1200  node.members.forEach((member) => {
1201    if (!isSystemapi(member)) {
1202      newMembers.push(member);
1203    }
1204  });
1205  node = ts.factory.updateEnumDeclaration(
1206    node,
1207    node.modifiers,
1208    node.name,
1209    newMembers
1210  );
1211  return node;
1212}
1213
1214/**
1215 * 处理struct子节点
1216 */
1217function processStructDeclaration(node) {
1218  const newMembers = [];
1219  node.members.forEach((member, index) => {
1220    if (index >= 1 && !isSystemapi(member)) {
1221      newMembers.push(member);
1222    }
1223  });
1224  node = ts.factory.updateStructDeclaration(
1225    node,
1226    node.modifiers,
1227    node.name,
1228    node.typeParameters,
1229    node.heritageClauses,
1230    newMembers
1231  );
1232  return node;
1233}
1234
1235/**
1236 * 解析reference
1237 * @param {string} url reference文件地址
1238 */
1239function resolveReferences(url) {
1240  const obj = referencesMap.get(url);
1241  let references = obj.references;
1242  if (!references || references.length === 0) {
1243    return;
1244  }
1245  for (let index = 0; index < references.length; index++) {
1246    const element = references[index];
1247    element.match(PATT.GET_REFERENCEURL);
1248    let referencePath = RegExp.$2;
1249    referencePath = referencesToOthers(referencePath, REFERENCE_TYPE.TOLOCAL);
1250    let fullReferencePath = path.resolve(path.dirname(url), referencePath);
1251    if (fs.existsSync(fullReferencePath) && !referencesModuleMap.has(fullReferencePath)) {
1252      const content = fs.readFileSync(fullReferencePath, 'utf-8'); //文件内容
1253      const fileName = processFileName(fullReferencePath);
1254      ts.transpileModule(content, {
1255        compilerOptions: COMPILER_OPTIONS,
1256        fileName: fileName,
1257        transformers: { before: [resolveCallback(fullReferencePath)] },
1258      });
1259    }
1260  }
1261}
1262function resolveCallback(url) {
1263  return (context) => {
1264    const allReferencesIdentifierSet = new Set([]);
1265    let allModule = {};
1266    return (node) => {
1267      const referenceSourceFile = node;
1268      node.statements.forEach((statement) => {
1269        if (
1270          ts.isModuleDeclaration(statement) &&
1271          statement.name &&
1272          ts.isStringLiteral(statement.name) &&
1273          statement.body &&
1274          ts.isModuleBlock(statement.body) &&
1275          !isSystemapi(statement)
1276        ) {
1277          ts.visitEachChild(statement, collectAllReferencesIdentifier, context);
1278          dealExternalStatements(referenceSourceFile);
1279          allModule[statement.name.text.toString()] = [...allReferencesIdentifierSet];
1280          allReferencesIdentifierSet.clear();
1281        }
1282      });
1283      referencesModuleMap.set(url, allModule);
1284      allModule = {};
1285      return node;
1286    };
1287    function dealExternalStatements(node) {
1288      node.statements.forEach((statement) => {
1289        let name = '';
1290        if (isSystemapi(statement)) {
1291          if (ts.isVariableStatement(statement)) {
1292            name = variableStatementGetEscapedText(statement);
1293          } else if (
1294            ts.isInterfaceDeclaration(statement) ||
1295            ts.isClassDeclaration(statement) ||
1296            ts.isEnumDeclaration(statement)
1297          ) {
1298            if (statement && statement.name && statement.name.escapedText) {
1299              name = statement.name.escapedText.toString();
1300            }
1301          }
1302          allReferencesIdentifierSet.delete(name);
1303        }
1304      });
1305    }
1306    function collectAllReferencesIdentifier(node) {
1307      if (isSystemapi(node)) {
1308        return node;
1309      }
1310      if (ts.isIdentifier(node)) {
1311        allReferencesIdentifierSet.add(node.escapedText.toString());
1312      }
1313      return ts.visitEachChild(node, collectAllReferencesIdentifier, context);
1314    }
1315  };
1316}
1317
1318function variableStatementGetEscapedText(statement) {
1319  let name = '';
1320  if (
1321    statement &&
1322    statement.declarationList &&
1323    statement.declarationList.declarations &&
1324    statement.declarationList.declarations.length > 0 &&
1325    statement.declarationList.declarations[0].name &&
1326    statement.declarationList.declarations[0].name.escapedText
1327  ) {
1328    name = statement.declarationList.declarations[0].name.escapedText.toString();
1329  }
1330  return name;
1331}
1332
1333function isSystemapi(node) {
1334  const notesContent = node.getFullText().replace(node.getText(), '').replace(/[\s]/g, '');
1335  const notesArr = notesContent.split(/\/\*\*/);
1336  const notesStr = notesArr[notesArr.length - 1];
1337  if (notesStr.length !== 0) {
1338    return /@systemapi/g.test(notesStr);
1339  }
1340  return false;
1341}
1342
1343/**
1344 * 判断是否为use static标记
1345 * @param { ts.Node } node
1346 * @returns { boolean }
1347 */
1348function isStaticFlag(node) {
1349  return ts.isExpressionStatement(node) &&
1350    node.expression &&
1351    ts.isStringLiteral(node.expression) &&
1352    node.expression.text &&
1353    node.expression.text === ARKTS_FLAG;
1354}
1355
1356function isEmptyFile(node) {
1357  let isEmpty = true;
1358  if (ts.isSourceFile(node) && node.statements) {
1359    for (let i = 0; i < node.statements.length; i++) {
1360      const statement = node.statements[i];
1361      if (ts.isImportDeclaration(statement) || isStaticFlag(statement)) {
1362        continue;
1363      }
1364      isEmpty = false;
1365      break;
1366    }
1367  }
1368  const fileName = getPureName(node.fileName.replace('.ts', '').replace('.static.ets', '').replace('.ets', ''));
1369  if (isEmpty && componentEtsFiles.includes(fileName)) {
1370    componentEtsDeleteFiles.push(fileName);
1371  }
1372  return isEmpty;
1373}
1374
1375let outputPath = '';
1376let inputDir = '';
1377start();
1378