1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as ts from 'typescript'; 17import fs from 'fs'; 18import path from 'path'; 19 20import { 21 IFileLog, 22 LogType, 23 startTimeStatisticsLocation, 24 stopTimeStatisticsLocation, 25 CompilationTimeStatistics 26} from './utils'; 27import { projectConfig } from '../main'; 28import { ModuleSourceFile } from './fast_build/ark_compiler/module/module_source_file'; 29import { collectKitModules } from './fast_build/system_api/rollup-plugin-system-api'; 30import { hasTsNoCheckOrTsIgnoreFiles, compilingEtsOrTsFiles } from './fast_build/ark_compiler/utils'; 31import { compilerOptions } from './ets_checker'; 32import createAstNodeUtils from './create_ast_node_utils'; 33 34/* 35* basic implementation logic: 36* tsc -> transformer 37* | -> iterate top-level static import/export declaration 38* | -> for each declaration 39* | -> collect KitInfo 40* | -> generate corresponding ohosImports for each ohos-source 41* | -> replace each origin declaration with corresponding ohosImports 42*/ 43 44export const kitTransformLog: IFileLog = new createAstNodeUtils.FileLog(); 45 46const KIT_PREFIX = '@kit.'; 47const KEEPTS = '// @keepTs'; 48 49/* 50* This API is the TSC Transformer for transforming `KitImport` into `OhosImport` 51* e.g. 52* ``` 53* import { ability, ErrorCode } from '@kit.AbilityKit' 54* ---> 55* import ability from '@ohos.ability.ability' 56* import ErrorCode from '@ohos.ability.errorCode' 57* ``` 58*/ 59export function processKitImport(id: string, metaInfo: Object, 60 compilationTime: CompilationTimeStatistics, shouldReturnOriginalNode: boolean = true): Function { 61 return (context: ts.TransformationContext) => { 62 const visitor: ts.Visitor = node => { 63 // only transform static import/export declaration 64 if (ts.isImportDeclaration(node) || (ts.isExportDeclaration(node) && node.moduleSpecifier)) { 65 const moduleRequest: string = (node.moduleSpecifier as ts.StringLiteral).text.replace(/'|"/g, ''); 66 if (moduleRequest.startsWith(KIT_PREFIX)) { 67 const kitDefs = getKitDefs(moduleRequest); 68 if (kitDefs && kitDefs.symbols) { 69 KitInfo.processKitInfo(moduleRequest, kitDefs.symbols as KitSymbols, node); 70 return [...KitInfo.getCurrentKitInfo().getOhosImportNodes()]; 71 } else { 72 kitTransformLog.errors.push({ 73 type: LogType.ERROR, 74 message: `Kit '${moduleRequest}' has no corresponding config file in ArkTS SDK. ` + 75 'Please make sure the Kit apis are consistent with SDK ' + 76 "and there's no local modification on Kit apis.", 77 pos: node.getStart() 78 }); 79 } 80 } 81 } 82 return node; 83 }; 84 85 return (node: ts.SourceFile) => { 86 startTimeStatisticsLocation(compilationTime ? compilationTime.processKitImportTime : undefined); 87 compilingEtsOrTsFiles.push(path.normalize(node.fileName)); 88 interceptLazyImportWithKitImport(node); 89 90 KitInfo.init(node, context, id); 91 92 // When compile hap or hsp, it is used to determine whether there is a keepTsNode in the file. 93 let hasKeepTs: boolean = false; 94 if (!projectConfig.complieHar) { 95 hasKeepTs = checkHasKeepTs(node); 96 } 97 98 if (projectConfig.processTs === true) { 99 if (ts.hasTsNoCheckOrTsIgnoreFlag(node) && !hasKeepTs) { 100 hasTsNoCheckOrTsIgnoreFiles.push(path.normalize(node.fileName)); 101 // process KitImport transforming 102 const processedNode: ts.SourceFile = 103 ts.visitEachChild(node, visitor, context); // this node used for [writeFile] 104 stopTimeStatisticsLocation(compilationTime ? compilationTime.processKitImportTime : undefined); 105 return processedNode; 106 } 107 // process [ConstEnum] + [TypeExportImport] + [KitImport] transforming 108 const processedNode: ts.SourceFile = 109 ts.visitEachChild(ts.getTypeExportImportAndConstEnumTransformer(context)(node), visitor, context); 110 ModuleSourceFile.newSourceFile(id, processedNode, metaInfo); 111 stopTimeStatisticsLocation(compilationTime ? compilationTime.processKitImportTime : undefined); 112 return shouldReturnOriginalNode ? node : processedNode; // this node not used for [writeFile] 113 } 114 // process KitImport transforming 115 const processedNode: ts.SourceFile = ts.visitEachChild(node, visitor, context); 116 stopTimeStatisticsLocation(compilationTime ? compilationTime.processKitImportTime : undefined); 117 return processedNode; 118 }; 119 }; 120} 121 122/** 123 * Kit does not support lazy-import yet, e.g.: import lazy {xxx} from '@kit.yyy' 124 */ 125function interceptLazyImportWithKitImport(node: ts.SourceFile): void { 126 if (node && node.statements) { 127 node.statements.forEach((statement) => { 128 if (ts.isImportDeclaration(statement) && statement.moduleSpecifier) { 129 const moduleRequest: string = (statement.moduleSpecifier as ts.StringLiteral).text.replace(/'|"/g, ''); 130 if (moduleRequest.startsWith(KIT_PREFIX) && statement.importClause && statement.importClause.isLazy) { 131 kitTransformLog.errors.push({ 132 type: LogType.ERROR, 133 message: `Can not use lazy import statement with Kit '${moduleRequest}', ` + 134 'Please remove the lazy keyword.', 135 pos: statement.getStart() 136 }); 137 } 138 } 139 }); 140 } 141} 142 143/* 144* Main implementation of Transforming 145*/ 146const DEFAULT_BINDINGS = 'default'; 147 148enum FileType { 149 ETS, 150 TS 151} 152 153interface KitSymbol { 154 source: string 155 bindings: string 156} 157 158declare type KitSymbols = Record<string, KitSymbol>; 159declare type TSspecifier = ts.ImportSpecifier | ts.ExportSpecifier; 160declare type TSModuleDeclaration = ts.ImportDeclaration | ts.ExportDeclaration; 161 162/* 163* class SpecificerInfo represents the corresponding info of each imported identifier which coming from Kit 164*/ 165class SpecificerInfo { 166 private localName: string; 167 private importName: string; 168 private symbol: KitSymbol; 169 private renamed: boolean; 170 171 private originElement: TSspecifier | undefined; 172 private tsImportSendableEnable : boolean = compilerOptions.tsImportSendableEnable; 173 174 constructor(localName: string, importName: string, symbol: KitSymbol, originElement: TSspecifier | undefined) { 175 this.localName = localName; 176 this.importName = importName; 177 this.symbol = symbol; 178 this.originElement = originElement; 179 this.renamed = (this.localName !== this.symbol.bindings); 180 181 this.validateImportingETSDeclarationSymbol(); 182 } 183 184 getSource(): string { 185 return this.symbol.source; 186 } 187 188 getLocalName(): string { 189 return this.localName; 190 } 191 192 isRenamed(): boolean { 193 return this.renamed; 194 } 195 196 getBindings(): string { 197 return this.symbol.bindings; 198 } 199 200 isDefaultBinding(): boolean { 201 return this.symbol.bindings === DEFAULT_BINDINGS; 202 } 203 204 validateImportingETSDeclarationSymbol() { 205 if (!this.tsImportSendableEnable && KitInfo.isTSFile() && /.d.ets$/.test(this.symbol.source)) { 206 kitTransformLog.errors.push({ 207 type: LogType.ERROR, 208 message: `Identifier '${this.importName}' comes from '${this.symbol.source}' ` + 209 `which can not be imported in .ts file.`, 210 pos: this.getOriginElementNode().getStart() 211 }); 212 } 213 } 214 215 setOriginElementNode(originElement: TSspecifier): void { 216 this.originElement = originElement; 217 } 218 219 getOriginElementNode(): TSspecifier { 220 return this.originElement; 221 } 222} 223 224export class KitInfo { 225 private static currentKitInfo: KitInfo = undefined; 226 private static currentFileType: FileType = FileType.ETS; 227 private static currentKitName: string = ''; 228 private static currentSourcefile: string = ''; 229 private static needSkipType: boolean = true; 230 private static tsEmitResolver: Object; 231 232 private symbols: KitSymbols; 233 private kitNode: TSModuleDeclaration; 234 private kitNodeModifier: readonly ts.Modifier[] | undefined; 235 private specifiers: Map<string, SpecificerInfo[]> = new Map<string, SpecificerInfo[]>(); 236 237 private ohosImportNodes: TSModuleDeclaration[] = []; 238 239 constructor(kitNode: TSModuleDeclaration, symbols: Record<string, KitSymbol>) { 240 this.kitNode = kitNode; 241 this.symbols = symbols; 242 243 this.kitNodeModifier = ts.canHaveDecorators(this.kitNode) ? ts.getModifiers(this.kitNode) : undefined; 244 } 245 246 static init(node: ts.SourceFile, context: ts.TransformationContext, moduleId: string): void { 247 // @ts-ignore 248 this.tsEmitResolver = context.getEmitResolver(); 249 this.currentSourcefile = moduleId; 250 if (/\.ts$/.test(node.fileName)) { 251 this.setFileType(FileType.TS); 252 } else { 253 this.setFileType(FileType.ETS); 254 } 255 256 if (projectConfig.processTs === true && !ts.hasTsNoCheckOrTsIgnoreFlag(node)) { 257 this.needSkipType = false; 258 } else { 259 this.needSkipType = true; 260 } 261 262 kitTransformLog.sourceFile = node; 263 } 264 265 static getCurrentKitName(): string { 266 return this.currentKitName; 267 } 268 269 static getCurrentKitInfo(): KitInfo { 270 return this.currentKitInfo; 271 } 272 273 static setFileType(fileType: FileType): void { 274 this.currentFileType = fileType; 275 } 276 277 static isTSFile(): boolean { 278 return this.currentFileType === FileType.TS; 279 } 280 281 static getCurrentSourcefile(): string { 282 return this.currentSourcefile; 283 } 284 285 static needSkipTypeSymbolOfNode(node: ts.Node): boolean { 286 if (!this.needSkipType) { 287 return false; 288 } 289 290 // need to skip type symbol 291 const resolver = this.tsEmitResolver; 292 let isTypeSymbol: boolean = false; 293 switch (node.kind) { 294 case ts.SyntaxKind.ImportDeclaration: 295 case ts.SyntaxKind.ImportClause: { 296 const importClause = ts.isImportClause(node) ? node : (node as ts.ImportDeclaration).importClause; 297 if (importClause) { 298 isTypeSymbol = importClause.isTypeOnly; 299 if (importClause.name && !resolver.isReferencedAliasDeclaration(importClause)) { 300 isTypeSymbol = true; 301 } 302 } 303 break; 304 } 305 case ts.SyntaxKind.ImportSpecifier: { 306 isTypeSymbol = (node as ts.ImportSpecifier).isTypeOnly; 307 if (!resolver.isReferencedAliasDeclaration(node)) { 308 isTypeSymbol = true; 309 } 310 break; 311 } 312 case ts.SyntaxKind.ExportDeclaration: { 313 isTypeSymbol = (node as ts.ExportDeclaration).isTypeOnly; 314 break; 315 } 316 case ts.SyntaxKind.ExportSpecifier: { 317 isTypeSymbol = (node as ts.ExportSpecifier).isTypeOnly; 318 if (!resolver.isValueAliasDeclaration(node)) { 319 isTypeSymbol = true; 320 } 321 break; 322 } 323 } 324 return isTypeSymbol; 325 } 326 327 static processImportDecl(kitNode: ts.ImportDeclaration, symbols: Record<string, KitSymbol>) { 328 if (!kitNode.importClause) { 329 // e.g. import "@kit.xxx" 330 this.currentKitInfo = new EmptyImportKitInfo(kitNode, symbols); 331 return; 332 } 333 334 if (kitNode.importClause!.namedBindings) { 335 const namedBindings: ts.NamedImportBindings = kitNode.importClause.namedBindings; 336 if (ts.isNamespaceImport(namedBindings)) { 337 // e.g. import * as ns from "@kit.xxx" 338 this.currentKitInfo = new NameSpaceKitInfo(kitNode, symbols); 339 } 340 if (ts.isNamedImports(namedBindings) && namedBindings.elements.length !== 0) { 341 // e.g. import { ... } from "@kit.xxx" 342 this.currentKitInfo = new ImportSpecifierKitInfo(kitNode, symbols); 343 namedBindings.elements.forEach(element => { this.currentKitInfo.collectSpecifier(element) }); 344 } 345 } 346 347 if (kitNode.importClause!.name && !this.needSkipTypeSymbolOfNode(kitNode.importClause)) { 348 // e.g. import default from "@kit.xxx" 349 const defaultName: string = kitNode.importClause.name.text; 350 if (!this.currentKitInfo) { 351 this.currentKitInfo = new ImportSpecifierKitInfo(kitNode, symbols); 352 } 353 this.currentKitInfo.newSpecificerInfo(defaultName, DEFAULT_BINDINGS, undefined); 354 } 355 } 356 357 static processExportDecl(kitNode: ts.ExportDeclaration, symbols: Record<string, KitSymbol>): void { 358 if (kitNode.exportClause) { 359 const namedExportBindings: ts.NamedExportBindings = kitNode.exportClause; 360 if (ts.isNamespaceExport(namedExportBindings)) { 361 // e.g. export * as ns from "@kit.xxx" 362 this.currentKitInfo = new NameSpaceKitInfo(kitNode, symbols); 363 } else if (ts.isNamedExports(namedExportBindings) && namedExportBindings.elements.length !== 0) { 364 // e.g. export { ... } from "@kit.xxx" 365 this.currentKitInfo = new ExportSpecifierKitInfo(kitNode, symbols); 366 namedExportBindings.elements.forEach(element => { this.currentKitInfo.collectSpecifier(element) }); 367 } 368 } else { 369 this.currentKitInfo = new ExportStarKitInfo(kitNode, symbols); 370 } 371 } 372 373 static processKitInfo(kitName: string, symbols: Record<string, KitSymbol>, kitNode: TSModuleDeclaration): void { 374 // clean up the currentKitInfo to prevent the following process getting 375 // the incorrect symbolTable with the KitInfo from last kit import. 376 this.currentKitInfo = undefined; 377 this.currentKitName = kitName; 378 379 // do not handle an empty import 380 if (ts.isImportDeclaration(kitNode)) { 381 // case 1: import { ... } from '@kit.xxx' 382 // case 2: import * as ns from '@kit.xxx' 383 // case 3: import defalutValue from '@kit.xxx' 384 // case 4: import '@kit.xxx' 385 this.processImportDecl(kitNode, symbols); 386 } 387 388 if (ts.isExportDeclaration(kitNode) && kitNode.moduleSpecifier) { 389 // case 1: export { ... } from '@kit.xxx' 390 // case 2: export * from '@kit.xxx' 391 // case 3: export * as ns from '@kit.xxx' - considering forbidden 392 this.processExportDecl(kitNode, symbols); 393 } 394 // transform into ohos imports or exports 395 this.currentKitInfo && this.currentKitInfo.transform(); 396 } 397 398 static cleanUp(): void { 399 this.currentKitInfo = undefined; 400 this.tsEmitResolver = undefined; 401 } 402 403 getSymbols(): KitSymbols { 404 return this.symbols; 405 } 406 407 getKitNode(): TSModuleDeclaration { 408 return this.kitNode; 409 } 410 411 getKitNodeModifier(): readonly ts.Modifier[] | undefined { 412 return this.kitNodeModifier; 413 } 414 415 getSpecifiers(): Map<string, SpecificerInfo[]> { 416 return this.specifiers; 417 } 418 419 getOhosImportNodes(): TSModuleDeclaration[] { 420 return this.ohosImportNodes; 421 } 422 423 newSpecificerInfo(localName: string, importName: string, originElement: TSspecifier | undefined): void { 424 const symbol: KitSymbol | undefined = this.symbols[importName]; 425 if (symbol) { 426 const specifier: SpecificerInfo = new SpecificerInfo(localName, importName, symbol, originElement); 427 if (this.specifiers.has(symbol.source)) { 428 this.specifiers.get(symbol.source).push(specifier); 429 } else { 430 this.specifiers.set(symbol.source, [specifier]); 431 } 432 } else { 433 kitTransformLog.errors.push({ 434 type: LogType.ERROR, 435 message: `'${importName}' is not exported from Kit '${KitInfo.getCurrentKitName()}.`, 436 pos: originElement ? originElement.getStart() : this.getKitNode().getStart() 437 }); 438 } 439 } 440 441 collectSpecifier(element: TSspecifier): void { 442 if (KitInfo.needSkipTypeSymbolOfNode(this.getKitNode()) || KitInfo.needSkipTypeSymbolOfNode(element)) { 443 // skip type symbol 444 return; 445 } 446 447 const localName: string = element.name.text; 448 const importName: string = element.propertyName ? element.propertyName.text : localName; 449 this.newSpecificerInfo(localName, importName, element); 450 } 451 452 // @ts-ignore 453 transform(): void {} //override api 454} 455 456class NameSpaceKitInfo extends KitInfo { 457 private namespaceName: string; 458 private localNameTable: string[] = []; 459 460 constructor(kitNode: ts.ImportDeclaration | ts.ExportDeclaration, symbols: Record<string, KitSymbol>) { 461 super(kitNode, symbols); 462 463 kitTransformLog.errors.push({ 464 type: LogType.ERROR, 465 message: `Namespace import or export of Kit is not supported currently.`, 466 pos: kitNode.getStart() 467 }); 468 } 469 470 transform(): void { 471 } 472} 473 474class ImportSpecifierKitInfo extends KitInfo { 475 private namedBindings: ts.ImportSpecifier[] = []; 476 private specifierDefaultName: ts.Identifier | undefined = undefined; 477 478 constructor(kitNode: ts.ImportDeclaration, symbols: Record<string, KitSymbol>) { 479 super(kitNode, symbols); 480 } 481 482 private hasNamedBindings(): boolean { 483 return this.namedBindings.length !== 0; 484 } 485 486 private clearSpecifierKitInfo(): void { 487 this.namedBindings = []; 488 this.specifierDefaultName = undefined; 489 } 490 491 transform(): void { 492 const node: ts.ImportDeclaration = this.getKitNode() as ts.ImportDeclaration; 493 494 this.getSpecifiers().forEach((specifiers: SpecificerInfo[], source: string) => { 495 collectKitModules(KitInfo.getCurrentSourcefile(), KitInfo.getCurrentKitName(), source); 496 specifiers.forEach((specifier: SpecificerInfo) => { 497 if (specifier.isDefaultBinding()) { 498 this.specifierDefaultName = ts.factory.createIdentifier(specifier.getLocalName()); 499 } else { 500 this.namedBindings.push( 501 ts.factory.createImportSpecifier( 502 specifier.getOriginElementNode() ? 503 (specifier.getOriginElementNode() as ts.ImportSpecifier).isTypeOnly : node.importClause.isTypeOnly, 504 specifier.isRenamed() ? ts.factory.createIdentifier(specifier.getBindings()) : undefined, 505 ts.factory.createIdentifier(specifier.getLocalName()) 506 ) 507 ); 508 } 509 }); 510 511 this.getOhosImportNodes().push(ts.factory.createImportDeclaration( 512 this.getKitNodeModifier(), 513 ts.factory.createImportClause( 514 node.importClause!.isTypeOnly, 515 this.specifierDefaultName, 516 this.hasNamedBindings() ? ts.factory.createNamedImports(this.namedBindings) : undefined 517 ), 518 ts.factory.createStringLiteral(trimSourceSuffix(source)) 519 )); 520 521 this.clearSpecifierKitInfo(); 522 }); 523 } 524} 525 526class EmptyImportKitInfo extends KitInfo { 527 constructor(kitNode: ts.ImportDeclaration, symbols: Record<string, KitSymbol>) { 528 super(kitNode, symbols); 529 530 /* 531 * Side-effect import can not be used by Kit since Kit actually has no spcific implementation. 532 * In general, a Kit may be imported in a Side-effect import statement by mistake. So we 533 * illustrate explicitly that Kit can not in Side-effect import to avoid misunderstanding 534 * of runtime's behavior. 535 */ 536 kitTransformLog.errors.push({ 537 type: LogType.ERROR, 538 message: `Can not use empty import(side-effect import) statement with Kit ` + 539 `'${(kitNode.moduleSpecifier as ts.StringLiteral).text.replace(/'|"/g, '')}', ` + 540 `Please specify imported symbols explicitly.`, 541 pos: kitNode.getStart() 542 }); 543 } 544 545 transform(): void { 546 } 547} 548 549class ExportSpecifierKitInfo extends KitInfo { 550 private namedBindings: ts.ExportSpecifier[] = []; 551 552 constructor(kitNode: ts.ExportDeclaration, symbols: Record<string, KitSymbol>) { 553 super(kitNode, symbols); 554 } 555 556 private hasNamedBindings(): boolean { 557 return this.namedBindings.length !== 0; 558 } 559 560 private clearSpecifierKitInfo(): void { 561 this.namedBindings = []; 562 } 563 564 transform(): void { 565 const node: ts.ExportDeclaration = this.getKitNode() as ts.ExportDeclaration; 566 567 this.getSpecifiers().forEach((specifiers: SpecificerInfo[], source: string) => { 568 specifiers.forEach((specifier: SpecificerInfo) => { 569 this.namedBindings.push( 570 ts.factory.createExportSpecifier( 571 (specifier.getOriginElementNode() as ts.ExportSpecifier).isTypeOnly, 572 specifier.isRenamed() ? ts.factory.createIdentifier(specifier.getBindings()) : undefined, 573 ts.factory.createIdentifier(specifier.getLocalName()) 574 ) 575 ); 576 }); 577 578 this.getOhosImportNodes().push(ts.factory.createExportDeclaration( 579 this.getKitNodeModifier(), 580 node.isTypeOnly, 581 this.hasNamedBindings() ? ts.factory.createNamedExports(this.namedBindings) : undefined, 582 ts.factory.createStringLiteral(trimSourceSuffix(source)), 583 node.assertClause 584 )); 585 586 this.clearSpecifierKitInfo(); 587 }); 588 } 589} 590 591class ExportStarKitInfo extends KitInfo { 592 private sourceSet: Set<string> = new Set<string>(); 593 594 constructor(kitNode: ts.ExportDeclaration, symbols: Record<string, KitSymbol>) { 595 super(kitNode, symbols); 596 597 for (const symbol in symbols) { 598 this.sourceSet.add(symbols[symbol].source); 599 } 600 601 kitTransformLog.errors.push({ 602 type: LogType.WARN, 603 message: `Using 'export *' will load all the sub-module of Kit in runtime.`, 604 pos: this.getKitNode().getStart() 605 }); 606 } 607 608 transform(): void { 609 const node: ts.ExportDeclaration = this.getKitNode() as ts.ExportDeclaration; 610 611 this.sourceSet.forEach((source: string) => { 612 this.getOhosImportNodes().push(ts.factory.createExportDeclaration( 613 this.getKitNodeModifier(), 614 node.isTypeOnly, 615 undefined, 616 ts.factory.createStringLiteral(trimSourceSuffix(source)), 617 node.assertClause 618 )); 619 }); 620 } 621} 622 623/* 624* utils part 625*/ 626const JSON_SUFFIX = '.json'; 627const KIT_CONFIGS = 'kit_configs'; 628const KIT_CONFIG_PATH = './build-tools/ets-loader/kit_configs'; 629 630function getKitDefs(kitModuleRequest: string): Object | undefined { 631 const kitConfigs: string[] = [path.resolve(__dirname, `../${KIT_CONFIGS}`)]; 632 if (process.env.externalApiPaths) { 633 const externalApiPaths = process.env.externalApiPaths.split(path.delimiter); 634 externalApiPaths.forEach(sdkPath => { 635 kitConfigs.push(path.resolve(sdkPath, KIT_CONFIG_PATH)); 636 }); 637 } 638 639 for (const kitConfig of kitConfigs) { 640 const kitModuleConfigJson = path.resolve(kitConfig, `./${kitModuleRequest}${JSON_SUFFIX}`); 641 if (fs.existsSync(kitModuleConfigJson)) { 642 return JSON.parse(fs.readFileSync(kitModuleConfigJson, 'utf-8')); 643 } 644 } 645 return undefined; 646} 647 648function trimSourceSuffix(source: string): string { 649 return source.replace(/\.d.[e]?ts$/, ''); 650} 651 652export function checkHasKeepTs(node: ts.SourceFile): boolean { 653 // Get the first comment in the file and determine whether it is "// @keepTs" 654 const comments = ts.getTrailingCommentRanges(node.getFullText(), 0) || []; 655 if (comments.length === 0) { 656 return false; 657 } 658 return node.getFullText().substring(comments[0].pos, comments[0].end).trim() === KEEPTS; 659} 660 661export function resetKitImportLog(): void { 662 kitTransformLog.cleanUp(); 663} 664 665export function cleanUpKitImportObjects(): void { 666 KitInfo.cleanUp(); 667 kitTransformLog.cleanUp(); 668}