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