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