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