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