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