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