1import { 2 __String, 3 Annotation, 4 AnnotationDeclaration, 5 AnnotationPropertyDeclaration, 6 CallExpression, 7 ClassDeclaration, 8 CommentDirectiveType, 9 CompilerOptions, 10 computeLineStarts, 11 concatenate, 12 Debug, 13 Decorator, 14 Diagnostic, 15 ElementAccessExpression, 16 EmitHost, 17 EmitTextWriter, 18 endsWith, 19 EnumMember, 20 EtsComponentExpression, 21 ExportDeclaration, 22 ExportSpecifier, 23 Expression, 24 ExpressionStatement, 25 factory, 26 flattenDiagnosticMessageText, 27 FunctionDeclaration, 28 getAllDecorators, 29 getAncestor, 30 getIllegalDecorators, 31 getLineAndCharacterOfPosition, 32 getRootEtsComponent, 33 getSourceFileOfNode, 34 Identifier, 35 ImportClause, 36 ImportDeclaration, 37 ImportEqualsDeclaration, 38 ImportSpecifier, 39 isCallExpression, 40 isEtsComponentExpression, 41 isExportSpecifier, 42 isExternalModuleImportEqualsDeclaration, 43 isFunctionDeclaration, 44 isIdentifier, 45 isImportClause, 46 isImportDeclaration, 47 isImportSpecifier, 48 isNamedExportBindings, 49 isNamedImportBindings, 50 isNamespaceImport, 51 isNamespaceExport, 52 isNodeModulesDirectory, 53 isPropertyAccessExpression, 54 isPropertyAssignment, 55 isStringLiteral, 56 isStructDeclaration, 57 isTypeAliasDeclaration, 58 isWhiteSpaceLike, 59 last, 60 LateVisibilityPaintedStatement, 61 LeftHandSideExpression, 62 LineAndCharacter, 63 MethodDeclaration, 64 Modifier, 65 ModifierLike, 66 Mutable, 67 NamedExports, 68 NamedImports, 69 NamedImportBindings, 70 NamespaceImport, 71 Node, 72 NodeArray, 73 NodeFactory, 74 NodeFlags, 75 nodeModulesPathPart, 76 noop, 77 ObjectLiteralExpression, 78 Path, 79 pathContainsNodeModules, 80 PropertyAccessExpression, 81 PropertyAssignment, 82 PropertyDeclaration, 83 resolvePath, 84 setTextRangePosEnd, 85 ScriptKind, 86 some, 87 SourceFile, 88 sys, 89 startsWith, 90 Statement, 91 stringContains, 92 StringLiteral, 93 StructDeclaration, 94 SyntaxKind, 95 TextRange, 96 TransformationContext, 97 TransformerFactory, 98 tryGetTextOfPropertyName, 99 visitEachChild, 100 visitLexicalEnvironment, 101 visitNode, 102 visitNodes, 103 VisitResult, 104 unescapeLeadingUnderscores, 105 normalizePath, 106} from "./_namespaces/ts"; 107 108/** @internal */ 109// Required for distinguishing annotations and decorators in other code analysis tools 110export const annotationMagicNamePrefix = "__$$ETS_ANNOTATION$$__"; 111const maxFlowDepthDefaultValue: number = 2000; 112 113/** @internal */ 114export function isInEtsFile(node: Node |undefined): boolean { 115 return node !== undefined && getSourceFileOfNode(node)?.scriptKind === ScriptKind.ETS; 116} 117/** @internal */ 118export function isInEtsFileWithOriginal(node: Node |undefined) { 119 while (node) { 120 node = node.original; 121 if (node !== undefined && getSourceFileOfNode(node)?.scriptKind === ScriptKind.ETS) { 122 return true; 123 } 124 } 125 return false; 126} 127 128/** @internal */ 129export function getReservedDecoratorsOfEtsFile(node: ClassDeclaration | StructDeclaration | FunctionDeclaration | MethodDeclaration | PropertyDeclaration, host: EmitHost): Decorator[] | undefined { 130 let reservedDecorators; 131 if (isInEtsFile(node)) { 132 reservedDecorators = ensureEtsDecorators(node, host); 133 } 134 return reservedDecorators; 135} 136 137/** @internal */ 138export function getReservedDecoratorsOfStructDeclaration(node: ClassDeclaration | StructDeclaration | FunctionDeclaration | MethodDeclaration | PropertyDeclaration, host: EmitHost): Decorator[] | undefined { 139 let reservedDecorators; 140 if (node.parent.kind === SyntaxKind.StructDeclaration) { 141 reservedDecorators = ensureEtsDecorators(node, host); 142 } 143 return reservedDecorators; 144} 145 146/** @internal */ 147export function ensureEtsDecorators(node: ClassDeclaration | StructDeclaration | FunctionDeclaration | MethodDeclaration | PropertyDeclaration, host: EmitHost): Decorator[] | undefined { 148 const allDecorators = getAllDecorators(node); 149 return getEffectiveDecorators(allDecorators, host); 150} 151 152export function concatenateDecoratorsAndModifiers(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined): readonly ModifierLike[] | undefined { 153 if (!decorators) return modifiers; 154 if (!modifiers) return decorators; 155 const decoratorsAndModifiers = concatenate<ModifierLike>(decorators, modifiers); 156 return decoratorsAndModifiers; 157} 158 159/** 160 * @internal 161 * Get the effective ETS Decorators for the node 162 * */ 163export function getEffectiveDecorators(decorators: readonly Decorator[] | NodeArray<Decorator> | undefined, host: EmitHost) { 164 const emitDecorators = host.getCompilerOptions().ets?.emitDecorators; 165 if (!emitDecorators) { 166 return undefined; 167 } 168 const reservedComponents: Decorator[] = []; 169 if (!decorators) { 170 return reservedComponents 171 } 172 173 for (let decorator of decorators) { 174 const expr = decorator.expression; 175 if (isIdentifier(expr)) { 176 for (const availableDecorator of emitDecorators) { 177 if (availableDecorator.name === expr.escapedText.toString()) { 178 reservedComponents.push(decorator); 179 break; 180 } 181 } 182 } 183 else if (isCallExpression(expr)) { 184 const childExpr = expr.expression; 185 if (isIdentifier(childExpr)) { 186 for (const availableDecorator of emitDecorators) { 187 if (availableDecorator.name === childExpr.escapedText.toString()) { 188 if (!availableDecorator.emitParameters) { 189 decorator = factory.updateDecorator( 190 decorator, 191 childExpr 192 ); 193 } 194 reservedComponents.push(decorator); 195 break; 196 } 197 } 198 } 199 } 200 } 201 202 return reservedComponents; 203} 204 205/** @internal */ 206export function inEtsStylesContext(input: LateVisibilityPaintedStatement | MethodDeclaration, host: EmitHost) { 207 if (!host.getCompilerOptions().ets?.styles.component || getSourceFileOfNode(input).scriptKind !== ScriptKind.ETS) { 208 return false; 209 } 210 const decorators: readonly Decorator[] = getAllDecorators(input); 211 212 if (decorators.length == 0) { 213 return false; 214 } 215 for (const decorator of decorators) { 216 if (isIdentifier(decorator.expression) && decorator.expression.escapedText.toString() === "Styles") { 217 return true; 218 } 219 } 220 return false; 221} 222 223export function isEtsFunctionDecorators(name: string | undefined, options: CompilerOptions): boolean { 224 const renderDecorators: string[] | undefined = options.ets?.render?.decorator; 225 return ((renderDecorators?.length && name && renderDecorators.includes(name)) || 226 name === options.ets?.styles?.decorator || (options.ets?.extend?.decorator?.includes(name as string) ?? false)); 227} 228 229export function isOhpm(packageManagerType: string | undefined): boolean { 230 return packageManagerType === "ohpm"; 231} 232 233export const ohModulesPathPart: string = "/oh_modules/"; 234export function isOHModules(modulePath: string): boolean { 235 return modulePath.indexOf(ohModulesPathPart) >= 0; 236} 237 238export function isOhpmAndOhModules(packageManagerType: string | undefined, modulePath: string): boolean { 239 return isOhpm(packageManagerType) && isOHModules(modulePath); 240} 241 242export function getModulePathPartByPMType(packageManagerType: string | undefined): string { 243 return isOhpm(packageManagerType) ? ohModulesPathPart : nodeModulesPathPart 244} 245 246export function getModuleByPMType(packageManagerType: string | undefined): string { 247 if (isOhpm(packageManagerType)) { 248 return "oh_modules"; 249 } 250 return "node_modules"; 251} 252 253export function getPackageJsonByPMType(packageManagerType: string | undefined): string { 254 if (isOhpm(packageManagerType)) { 255 return "oh-package.json5"; 256 } 257 return "package.json"; 258} 259 260export function isOHModulesDirectory(dirPath: Path) { 261 return endsWith(dirPath, "/oh_modules"); 262} 263 264export function isTargetModulesDerectory(dirPath: Path): boolean { 265 return isNodeModulesDirectory(dirPath) || isOHModulesDirectory(dirPath); 266} 267export function pathContainsOHModules(path: string): boolean { 268 return stringContains(path, ohModulesPathPart); 269} 270 271export function choosePathContainsModules(packageManagerType: string | undefined, fileName: string): boolean { 272 return isOhpm(packageManagerType) ? pathContainsOHModules(fileName) : pathContainsNodeModules(fileName); 273} 274 275/** @internal */ 276export function isOHModulesAtTypesDirectory(dirPath: Path) { 277 return endsWith(dirPath, "/oh_modules/@types"); 278} 279 280/** @internal */ 281export function isOHModulesReference(fileName: string): boolean { 282 return startsWith(fileName, "oh_modules/") || pathContainsOHModules(fileName); 283} 284 285/** @internal */ 286export function isArkTsDecorator(node: Node, compilerOptions?: CompilerOptions): boolean { 287 if (compilerOptions) { 288 return hasEtsExtendDecoratorNames(getAllDecorators(node), compilerOptions) || 289 hasEtsStylesDecoratorNames(getAllDecorators(node), compilerOptions) || 290 hasEtsBuilderDecoratorNames(getAllDecorators(node), compilerOptions) || 291 hasEtsConcurrentDecoratorNames(getAllDecorators(node), compilerOptions); 292 } 293 return false; 294} 295 296/** @internal */ 297export function hasEtsExtendDecoratorNames(decorators: NodeArray<Decorator> | readonly Decorator[] | undefined, options: CompilerOptions): boolean { 298 const names: string[] = []; 299 if (!decorators || !decorators.length) { 300 return false; 301 } 302 decorators.forEach(decorator => { 303 const nameExpr = decorator.expression; 304 if (isCallExpression(nameExpr) && isIdentifier(nameExpr.expression) && 305 options.ets?.extend.decorator?.includes(nameExpr.expression.escapedText.toString())) { 306 names.push(nameExpr.expression.escapedText.toString()); 307 } 308 }); 309 return names.length !== 0; 310} 311 312/** @internal */ 313export function hasEtsStylesDecoratorNames(decorators: NodeArray<Decorator> | readonly Decorator[] | undefined, options: CompilerOptions): boolean { 314 const names: string[] = []; 315 if (!decorators || !decorators.length) { 316 return false; 317 } 318 decorators.forEach(decorator => { 319 const nameExpr = decorator.expression; 320 if (isIdentifier(nameExpr) && nameExpr.escapedText.toString() === options.ets?.styles?.decorator) { 321 names.push(nameExpr.escapedText.toString()); 322 } 323 }); 324 return names.length !== 0; 325} 326 327/** @internal */ 328export function hasEtsBuildDecoratorNames(decorators: NodeArray<Decorator> | readonly Decorator[] | undefined, options: CompilerOptions): boolean { 329 const names: string[] = []; 330 if (!decorators || !decorators.length) { 331 return false; 332 } 333 decorators.forEach(decorator => { 334 const nameExpr = decorator.expression; 335 if (isIdentifier(nameExpr) && options.ets?.render?.method.indexOf(nameExpr.escapedText.toString()) !== -1) { 336 names.push(nameExpr.escapedText.toString()); 337 } 338 }); 339 return names.length !== 0; 340} 341 342/** @internal */ 343export function hasEtsBuilderDecoratorNames(decorators: NodeArray<Decorator> | readonly Decorator[] | undefined, options: CompilerOptions): boolean { 344 const names: string[] = []; 345 if (!decorators || !decorators.length) { 346 return false; 347 } 348 const renderDecorators: string[] | undefined = options?.ets?.render?.decorator; 349 if (!(renderDecorators && renderDecorators.length)) { 350 return false; 351 } 352 decorators.forEach(decorator => { 353 const nameExpr = decorator.expression; 354 if (isIdentifier(nameExpr) && renderDecorators.includes(nameExpr.escapedText.toString())) { 355 names.push(nameExpr.escapedText.toString()); 356 } 357 }); 358 return names.length !== 0; 359} 360 361/** @internal */ 362export function hasEtsConcurrentDecoratorNames(decorators: NodeArray<Decorator> | readonly Decorator[] | undefined, options: CompilerOptions): boolean { 363 const names: string[] = []; 364 if (!decorators || !decorators.length) { 365 return false; 366 } 367 decorators.forEach(decorator => { 368 const nameExpr = decorator.expression; 369 if (isIdentifier(nameExpr) && nameExpr.escapedText.toString() === options.ets?.concurrent?.decorator) { 370 names.push(nameExpr.escapedText.toString()); 371 } 372 }); 373 return names.length !== 0; 374} 375 376/** @internal */ 377export function isTokenInsideBuilder(decorators: NodeArray<Decorator> | readonly Decorator[] | undefined, compilerOptions: CompilerOptions): boolean { 378 const renderDecorators = compilerOptions.ets?.render?.decorator ?? ["Builder", "LocalBuilder"]; 379 380 if (!decorators) { 381 return false; 382 } 383 384 if (!(renderDecorators && renderDecorators.length)) { 385 return false; 386 } 387 388 for (const decorator of decorators) { 389 if (isIdentifier(decorator.expression) && renderDecorators.includes((<Identifier>(decorator.expression)).escapedText.toString())) { 390 return true; 391 } 392 } 393 return false; 394} 395 396/** @internal */ 397export function getEtsComponentExpressionInnerCallExpressionNode(node: Node | undefined): EtsComponentExpression | undefined { 398 while (node && node.kind !== SyntaxKind.EtsComponentExpression) { 399 if (node.kind === SyntaxKind.CallExpression) { 400 node = (<CallExpression>node).expression; 401 } 402 else if (node.kind === SyntaxKind.PropertyAccessExpression) { 403 node = (<PropertyAccessExpression>node).expression; 404 } 405 else { 406 node = undefined; 407 } 408 } 409 return <EtsComponentExpression>node; 410} 411 412/** @internal */ 413export function getRootEtsComponentInnerCallExpressionNode(node: Node | undefined): EtsComponentExpression | undefined { 414 if (node && isEtsComponentExpression(node)) { 415 return node; 416 } 417 418 while (node) { 419 const ancestor = <CallExpression>getAncestor(isCallExpression(node) ? node.parent : node, SyntaxKind.CallExpression); 420 const target = getRootEtsComponent(ancestor); 421 if (target && isInStateStylesObject(node)) { 422 return target; 423 } 424 node = ancestor ?? node.parent; 425 } 426 427 return undefined; 428} 429 430/** @internal */ 431export function getEtsComponentExpressionInnerExpressionStatementNode(node: Node | undefined): CallExpression | EtsComponentExpression | PropertyAccessExpression | undefined { 432 while (node && !isIdentifier(node)) { 433 const parent = node; 434 const currentNode = (node as ExpressionStatement | CallExpression | PropertyAccessExpression | EtsComponentExpression).expression; 435 if (currentNode && isIdentifier(currentNode)) { 436 node = parent; 437 break; 438 } 439 else { 440 node = currentNode; 441 } 442 } 443 if (!node) { 444 return undefined; 445 } 446 if (isCallExpression(node) || isEtsComponentExpression(node) || isPropertyAccessExpression(node)) { 447 return node; 448 } 449 return undefined; 450} 451 452/** @internal */ 453function isInStateStylesObject(node: Node | undefined): boolean { 454 const ancestor = <ObjectLiteralExpression>getAncestor(node, SyntaxKind.ObjectLiteralExpression); 455 return ancestor !== undefined && ancestor.parent !== undefined && isPropertyAssignment(ancestor.parent); 456} 457 458/** @internal */ 459export function getEtsExtendDecoratorsComponentNames(decorators: NodeArray<Decorator> | readonly Decorator[] | undefined, compilerOptions: CompilerOptions): __String[] { 460 const extendComponents: __String[] = []; 461 const extendDecorator = compilerOptions.ets?.extend?.decorator ?? "Extend"; 462 decorators?.forEach((decorator) => { 463 if (decorator.expression.kind === SyntaxKind.CallExpression) { 464 const identifier = (<CallExpression>decorator.expression).expression; 465 const args = (<CallExpression>decorator.expression).arguments; 466 if (identifier.kind === SyntaxKind.Identifier && extendDecorator?.includes((<Identifier>identifier).escapedText.toString()) && args.length) { 467 // only read @Extend(...args) first argument 468 if (args[0].kind === SyntaxKind.Identifier) { 469 extendComponents.push((<Identifier>args[0]).escapedText); 470 } 471 } 472 } 473 }); 474 return extendComponents; 475} 476 477/** @internal */ 478export function getEtsStylesDecoratorComponentNames(decorators: NodeArray<Decorator> | readonly Decorator[] | undefined, compilerOptions: CompilerOptions): __String[] { 479 const stylesComponents: __String[] = []; 480 const stylesDecorator = compilerOptions.ets?.styles?.decorator ?? "Styles"; 481 decorators?.forEach(decorator => { 482 if (decorator.kind === SyntaxKind.Decorator && decorator.expression.kind === SyntaxKind.Identifier) { 483 const identifier = <Identifier>decorator.expression; 484 if (identifier.kind === SyntaxKind.Identifier && identifier.escapedText === stylesDecorator) { 485 stylesComponents.push(identifier.escapedText); 486 } 487 } 488 }); 489 return stylesComponents; 490} 491 492/** @internal */ 493export function filterEtsExtendDecoratorComponentNamesByOptions(decoratorComponentNames: __String[], compilerOptions: CompilerOptions): __String[] { 494 if (!decoratorComponentNames.length) { 495 return []; 496 } 497 const filtered: __String[] = []; 498 compilerOptions.ets?.extend.components.forEach(({ name }) => { 499 if (name === last(decoratorComponentNames)) { 500 filtered.push(name); 501 } 502 }); 503 return filtered; 504} 505 506export function getTypeExportImportAndConstEnumTransformer(context: TransformationContext): (node: SourceFile) => SourceFile { 507 return transformTypeExportImportAndConstEnumInTypeScript(context); 508} 509 510export function getAnnotationTransformer(): TransformerFactory<SourceFile> { 511 return (context: TransformationContext) => transformAnnotation(context); 512} 513 514export function transformAnnotation(context: TransformationContext): (node: SourceFile) => SourceFile { 515 const resolver = context.getEmitResolver(); 516 517 return transformSourceFile; 518 519 function transformSourceFile(node: SourceFile): SourceFile { 520 if (node.isDeclarationFile) { 521 return node; 522 } 523 // Firstly, visit declarations 524 const updatedSource = factory.updateSourceFile(node, 525 visitLexicalEnvironment(node.statements, visitAnnotationsDeclarations, context)); 526 // Secondly, visit import and usage of annotations 527 return factory.updateSourceFile( 528 node, 529 visitLexicalEnvironment(updatedSource.statements, visitAnnotations, context)); 530 } 531 532 function visitAnnotationsDeclarations(node: Node): VisitResult<Node> { 533 switch (node.kind) { 534 case SyntaxKind.AnnotationDeclaration: 535 return visitAnnotationDeclaration(<AnnotationDeclaration>node); 536 default: 537 return visitEachChild(node, visitAnnotationsDeclarations, context); 538 } 539 } 540 541 function visitAnnotations(node: Node): VisitResult<Node> { 542 switch (node.kind) { 543 case SyntaxKind.ImportSpecifier: 544 return visitImportSpecifier(<ImportSpecifier>node); 545 case SyntaxKind.Decorator: 546 return visitAnnotation(<Annotation>node); 547 default: 548 return visitEachChild(node, visitAnnotations, context); 549 } 550 } 551 552 function visitImportSpecifier(node: ImportSpecifier): VisitResult<ImportSpecifier> { 553 // Return if the import has type or not refered to Annotation 554 if (node.isTypeOnly || !resolver.isReferredToAnnotation(node)) { 555 return node; 556 } 557 const magicPrefixName = addMagicPrefixToAnnotationNameIdentifier(node.name); 558 Debug.assert(isIdentifier(magicPrefixName)); 559 // Add magic prefix for import Annotation. For example, 560 // import {Anno} from "xxx" ---> import {__$$ETS_ANNOTATION$$__Anno} from "xxx" 561 return factory.updateImportSpecifier(node, node.isTypeOnly, node.propertyName, magicPrefixName); 562 } 563 564 function visitAnnotationDeclaration(node: AnnotationDeclaration): VisitResult<AnnotationDeclaration> { 565 // Add magic prefix for AnnotationDeclaration. For example, 566 // @interface Anno {} ---> @interface __$$ETS_ANNOTATION$$__Anno {} 567 const magicPrefixName = addMagicPrefixToAnnotationNameIdentifier(node.name); 568 Debug.assert(isIdentifier(magicPrefixName)); 569 // Add explicit type annotation and initializer. For example, 570 // @interface Anno { 571 // a = 10 + 5 572 // } 573 // 574 // will be transformed to 575 // 576 // @interface __$$ETS_ANNOTATION$$__Anno { 577 // a: number = 15 578 // } 579 const members = node.members.map((node: AnnotationPropertyDeclaration) => { 580 const type = resolver.getAnnotationPropertyInferredType(node); 581 const initializer = resolver.getAnnotationPropertyEvaluatedInitializer(node); 582 return factory.updateAnnotationPropertyDeclaration(node, node.name, type, initializer); 583 }); 584 585 return factory.updateAnnotationDeclaration(node, node.modifiers, magicPrefixName, members); 586 } 587 588 function visitAnnotation(node: Annotation): VisitResult<Annotation> { 589 if (!node.annotationDeclaration) { 590 return node; 591 } 592 // Add default values into annotation object literal. For example, 593 // @interface Anno { 594 // a: number = 10 595 // b: string 596 // } 597 // 598 // @Anno({b: "abc"}) --- > @Anno({a: 10, b: "abc"}) 599 // class C {} 600 // 601 // and 602 // 603 // Add the magic prefix for annotation name. For example, 604 // @myModule.Anno({a: 10, b: "abc"}) --- > @myModule.__$$ETS_ANNOTATION$$__Anno({a: 10, b: "abc"}) 605 return factory.updateDecorator( 606 node, 607 addMagicPrefixToAnnotationNameIdentifier(addDefaultValuesIntoAnnotationObjectLiteral(node)), 608 node.annotationDeclaration 609 ); 610 } 611 612 function addMagicPrefixToAnnotationNameIdentifier(expr: Expression): Identifier | PropertyAccessExpression | CallExpression { 613 switch (expr.kind) { 614 case SyntaxKind.Identifier: 615 return factory.createIdentifier( 616 annotationMagicNamePrefix + (expr as Identifier).escapedText 617 ); 618 case SyntaxKind.PropertyAccessExpression: 619 const propAccessExpr = expr as PropertyAccessExpression; 620 return factory.updatePropertyAccessExpression( 621 propAccessExpr, 622 propAccessExpr.expression, 623 addMagicPrefixToAnnotationNameIdentifier(propAccessExpr.name) as Identifier 624 ); 625 case SyntaxKind.CallExpression: 626 const callExpr = expr as CallExpression; 627 return factory.updateCallExpression( 628 callExpr, 629 addMagicPrefixToAnnotationNameIdentifier(callExpr.expression), 630 callExpr.typeArguments, 631 callExpr.arguments 632 ); 633 default: 634 return expr as (Identifier | PropertyAccessExpression | CallExpression); 635 } 636 } 637 638 function addDefaultValuesIntoAnnotationObjectLiteral(annotation: Annotation): CallExpression { 639 Debug.assert(annotation.annotationDeclaration); 640 const members = annotation.annotationDeclaration.members; 641 if (isIdentifier(annotation.expression) || 642 isPropertyAccessExpression(annotation.expression) || 643 (isCallExpression(annotation.expression) && annotation.expression.arguments.length === 0)) { 644 const defaultValues = new Array<PropertyAssignment>(members.length); 645 for (let i = 0; i < members.length; ++i) { 646 const member = members[i] as AnnotationPropertyDeclaration; 647 const initializer = resolver.getAnnotationPropertyEvaluatedInitializer(member); 648 Debug.assert(initializer !== undefined); 649 defaultValues[i] = factory.createPropertyAssignment(member.name, initializer!); 650 } 651 const newCallExpr = factory.createCallExpression( 652 annotation.expression, 653 /* typeArguments */ undefined, 654 [factory.createObjectLiteralExpression(defaultValues, false)]); 655 return newCallExpr; 656 } 657 else if (isCallExpression(annotation.expression)) { 658 Debug.assert(annotation.expression.arguments.length === 1); 659 const obj = annotation.expression.arguments[0] as ObjectLiteralExpression; 660 const objPropNameToProp = new Map<__String, PropertyAssignment>(); 661 obj.properties.forEach(p => { 662 Debug.assert(isPropertyAssignment(p)); 663 objPropNameToProp.set(tryGetTextOfPropertyName(p.name)!, p); 664 }); 665 666 const defaultValues = new Array<PropertyAssignment>(members.length); 667 for (let i = 0; i < members.length; ++i) { 668 const member = members[i] as AnnotationPropertyDeclaration; 669 const memberName = tryGetTextOfPropertyName(member.name)!; 670 if (objPropNameToProp.has(memberName)) { 671 const evaluatedProps = resolver.getAnnotationObjectLiteralEvaluatedProps(annotation); 672 Debug.assert(evaluatedProps !== undefined); 673 defaultValues[i] = factory.createPropertyAssignment(member.name, evaluatedProps.get(memberName)!); 674 } 675 else { 676 const evaluatedInitializer = resolver.getAnnotationPropertyEvaluatedInitializer(member); 677 Debug.assert(evaluatedInitializer !== undefined); 678 defaultValues[i] = factory.createPropertyAssignment(member.name, evaluatedInitializer); 679 } 680 } 681 const newCallExpr = factory.updateCallExpression( 682 annotation.expression, 683 annotation.expression.expression, 684 /* typeArguments */ undefined, 685 [factory.updateObjectLiteralExpression(obj, defaultValues)]); 686 return newCallExpr; 687 } 688 Debug.fail(); 689 } 690} 691 692/** 693 * Add 'type' flag to import/export when import/export an type member. 694 * Replace const enum with number and string literal. 695 */ 696export function transformTypeExportImportAndConstEnumInTypeScript(context: TransformationContext): (node: SourceFile) => SourceFile { 697 const resolver = context.getEmitResolver(); 698 interface ImportInfo { 699 name: Identifier | undefined, 700 namespaceImport: NamespaceImport | undefined, 701 namedImports: ImportSpecifier[] 702 }; 703 interface ExportInfo { 704 namedExports: ExportSpecifier[] 705 }; 706 707 // recore type import/export info to create new import/export type statement 708 let currentTypeImportInfo: ImportInfo; 709 let currentTypeExportInfo: ExportInfo; 710 711 return transformSourceFile; 712 713 function transformSourceFile(node: SourceFile): SourceFile { 714 if (node.isDeclarationFile) { 715 return node; 716 } 717 const visited = factory.updateSourceFile( 718 node, 719 visitLexicalEnvironment(node.statements, visitImportExportAndConstEnumMember, context)); 720 return visited; 721 } 722 723 function visitImportExportAndConstEnumMember(node: Node): VisitResult<Node> { 724 switch (node.kind) { 725 case SyntaxKind.ImportDeclaration: 726 return visitImportDeclaration(<ImportDeclaration>node); 727 case SyntaxKind.ImportEqualsDeclaration: 728 return visitImportEqualsDeclaration(<ImportEqualsDeclaration>node); 729 case SyntaxKind.ExportDeclaration: 730 return visitExportDeclaration(<ExportDeclaration>node); 731 case SyntaxKind.PropertyAccessExpression: 732 case SyntaxKind.ElementAccessExpression: 733 return visitConstEnum(<PropertyAccessExpression | ElementAccessExpression>node); 734 case SyntaxKind.EnumMember: 735 return visitEnumMember(<EnumMember>node); 736 default: 737 return visitEachChild(node, visitImportExportAndConstEnumMember, context); 738 } 739 } 740 741 /** 742 * Transform: 743 * 744 * import a, {b, c} from ... 745 * 746 * To: 747 * 748 * import {b} from ... 749 * import type a from ... 750 * import type {c} from ... 751 * 752 * when 'a' and 'c' are type. 753 */ 754 function visitImportDeclaration(node: ImportDeclaration): VisitResult<Statement> { 755 // return if the import already has 'type' 756 if (!node.importClause || node.importClause.isTypeOnly) { 757 return node; 758 } 759 resetcurrentTypeImportInfo(); 760 const res: Statement[] = []; 761 const importClause = visitNode(node.importClause, visitImportClause, isImportClause); 762 if (importClause) { 763 res.push(factory.updateImportDeclaration(node, /*modifiers*/ undefined, 764 importClause, node.moduleSpecifier, /*assertClause*/ undefined)); 765 } 766 // create new import statement with 'type' 767 const typeImportClauses = createTypeImportClause(); 768 for (const typeImportClause of typeImportClauses) { 769 res.push(factory.createImportDeclaration(/*modifiers*/ undefined, 770 typeImportClause, node.moduleSpecifier)); 771 } 772 return res.length > 0 ? res : undefined; 773 } 774 775 function visitImportClause(node: ImportClause): VisitResult<ImportClause> { 776 if (node.isTypeOnly) { 777 return node; 778 } 779 let name: Identifier | undefined; 780 if (resolver.isReferencedAliasDeclaration(node)) { 781 name = node.name; 782 } 783 // consider it is a type if the symbol has referenced. 784 else if (resolver.isReferenced(node)) { 785 addTypeImportClauseName(node); 786 } 787 const namedBindings = visitNode(node.namedBindings, visitNamedImportBindings, isNamedImportBindings); 788 return (name || namedBindings) ? 789 factory.updateImportClause(node, /*isTypeOnly*/ false, name, namedBindings) : 790 undefined; 791 } 792 793 function visitNamedImportBindings(node: NamedImportBindings): VisitResult<NamedImportBindings> { 794 if (node.kind === SyntaxKind.NamespaceImport) { 795 if (resolver.isReferencedAliasDeclaration(node)) { 796 return node; 797 } 798 if (resolver.isReferenced(node)) { 799 addTypeNamespaceImport(node); 800 } 801 return undefined; 802 } 803 else { 804 const elements = visitNodes(node.elements, visitImportSpecifier, isImportSpecifier); 805 return some(elements) ? factory.updateNamedImports(node, elements) : undefined; 806 } 807 } 808 809 function visitImportSpecifier(node: ImportSpecifier): VisitResult<ImportSpecifier> { 810 if (node.isTypeOnly) { 811 return node; 812 } 813 if (resolver.isReferencedAliasDeclaration(node)) { 814 return node; 815 } 816 if (resolver.isReferenced(node)) { 817 addTypeImportSpecifier(node); 818 } 819 return undefined; 820 } 821 822 function addTypeImportClauseName(node: ImportClause): void { 823 currentTypeImportInfo.name = node.name; 824 } 825 826 function addTypeNamespaceImport(node: NamespaceImport): void { 827 currentTypeImportInfo.namespaceImport = node; 828 } 829 830 function addTypeImportSpecifier(node: ImportSpecifier): void { 831 currentTypeImportInfo.namedImports.push(node); 832 } 833 834 /** 835 * Create new import type statement, like: 836 * import type {a} from ... 837 */ 838 function createTypeImportClause(): ImportClause[] { 839 const name: Identifier | undefined = currentTypeImportInfo.name; 840 let namedBindings: NamedImportBindings | undefined; 841 if (currentTypeImportInfo.namespaceImport) { 842 namedBindings = currentTypeImportInfo.namespaceImport; 843 } 844 else if (currentTypeImportInfo.namedImports.length > 0) { 845 namedBindings = factory.createNamedImports(currentTypeImportInfo.namedImports); 846 } 847 const typeImportClauses: ImportClause[] = []; 848 if (name !== undefined) { 849 typeImportClauses.push(factory.createImportClause(/*isTypeOnly*/ true, name, /*namedBindings*/ undefined)); 850 } 851 if (namedBindings !== undefined) { 852 typeImportClauses.push(factory.createImportClause(/*isTypeOnly*/ true, /*name*/ undefined, namedBindings)); 853 } 854 resetcurrentTypeImportInfo(); 855 return typeImportClauses; 856 } 857 858 /** 859 * Transform: 860 * 861 * import a = require(...) 862 * 863 * To: 864 * 865 * import type a = require(...) 866 * 867 * when 'a' is type. 868 */ 869 function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult<Statement> { 870 // return if the import already has 'type' 871 if (node.isTypeOnly) { 872 return node; 873 } 874 875 if (isExternalModuleImportEqualsDeclaration(node)) { 876 const isReferenced = resolver.isReferencedAliasDeclaration(node); 877 if (isReferenced) { 878 return node; 879 } 880 if (resolver.isReferenced(node)) { 881 return factory.updateImportEqualsDeclaration(node, node.modifiers, 882 /*isTypeOnly*/ true, node.name, node.moduleReference); 883 } 884 885 return undefined; 886 } 887 888 return node; 889 } 890 891 /** 892 * Transform: 893 * 894 * export {a} 895 * 896 * To: 897 * 898 * export type {a} 899 * 900 * when 'a' is type. 901 */ 902 function visitExportDeclaration(node: ExportDeclaration): VisitResult<Statement> { 903 // return if the export already has 'type'or export * 904 if (node.isTypeOnly || !node.exportClause || isNamespaceExport(node.exportClause)) { 905 return node; 906 } 907 908 resetcurrentTypeExportInfo(); 909 const res: Statement[] = []; 910 911 const exportClause = visitNode(node.exportClause, visitNamedExports, isNamedExportBindings); 912 if (exportClause) { 913 res.push(factory.updateExportDeclaration(node, /*modifiers*/ undefined, 914 node.isTypeOnly, exportClause, node.moduleSpecifier, /*assertClause*/ undefined)); 915 } 916 const typeExportClause = createTypeExportClause(); 917 if (typeExportClause) { 918 res.push(factory.createExportDeclaration(/*modifiers*/ undefined, 919 /*isTypeOnly*/ true, typeExportClause, node.moduleSpecifier)); 920 } 921 922 return res.length > 0 ? res : undefined; 923 } 924 925 function visitNamedExports(node: NamedExports): VisitResult<NamedExports> { 926 const elements = visitNodes(node.elements, visitExportSpecifier, isExportSpecifier); 927 return some(elements) ? factory.updateNamedExports(node, elements) : undefined; 928 } 929 930 function visitExportSpecifier(node: ExportSpecifier): VisitResult<ExportSpecifier> { 931 if (node.isTypeOnly) { 932 return node; 933 } 934 if (resolver.isValueAliasDeclaration(node)) { 935 return node; 936 } 937 // consider all rest member are type. 938 addTypeExportSpecifier(node); 939 return undefined; 940 } 941 942 function addTypeExportSpecifier(node: ExportSpecifier): void { 943 currentTypeExportInfo.namedExports.push(node); 944 } 945 946 /** 947 * Create new export type statement, like: 948 * export type {a} 949 */ 950 function createTypeExportClause(): NamedExports | undefined { 951 let namedBindings: NamedExports | undefined; 952 if (currentTypeExportInfo.namedExports.length > 0) { 953 namedBindings = factory.createNamedExports(currentTypeExportInfo.namedExports); 954 } 955 resetcurrentTypeExportInfo(); 956 return namedBindings; 957 } 958 959 function visitConstEnum(node: PropertyAccessExpression | ElementAccessExpression): LeftHandSideExpression { 960 const constantValue = resolver.getConstantValue(node); 961 if (constantValue !== undefined) { 962 const substitute = typeof constantValue === "string" ? 963 factory.createStringLiteral(constantValue) : 964 factory.createNumericLiteral(constantValue); 965 return substitute; 966 } 967 968 return visitEachChild(node, visitImportExportAndConstEnumMember, context); 969 } 970 971 /** 972 * If the enum member is a const value, replace it. 973 */ 974 function visitEnumMember(node: EnumMember): VisitResult<EnumMember> { 975 const value = resolver.getConstantValue(node); 976 if (value !== undefined) { 977 const substitute = typeof value === "string" ? 978 factory.createStringLiteral(value) : 979 factory.createNumericLiteral(value); 980 return factory.updateEnumMember(node, node.name, substitute); 981 } 982 return visitEachChild(node, visitImportExportAndConstEnumMember, context); 983 } 984 985 function resetcurrentTypeImportInfo(): void { 986 currentTypeImportInfo = { name: undefined, namespaceImport: undefined, namedImports:[] }; 987 } 988 989 function resetcurrentTypeExportInfo(): void { 990 currentTypeExportInfo = { namedExports:[] }; 991 } 992} 993 994export function hasTsNoCheckOrTsIgnoreFlag(node: SourceFile): boolean { 995 // check @ts-nocheck flag 996 if (!!node.checkJsDirective && node.checkJsDirective.enabled === false) { 997 return true; 998 } 999 // check @ts-ignore flag 1000 if (node.commentDirectives !== undefined) { 1001 for (const commentDirective of node.commentDirectives) { 1002 if (commentDirective.type === CommentDirectiveType.Ignore) { 1003 return true; 1004 } 1005 } 1006 } 1007 return false; 1008} 1009 1010export function createObfTextSingleLineWriter(): EmitTextWriter { 1011 const space: string = " "; 1012 let output: string; 1013 let lineStart: boolean; 1014 let linePos: number; 1015 let lineCount: number; 1016 // If the last character of string is the one of below chars, there is no need to write space again. 1017 const noSpaceTrailingChars: Set<string> = new Set([' ', ';', ',', '(', ')', '{', '}']); 1018 1019 function updateLineCountAndPosFor(s: string) { 1020 const lineStartsOfS = computeLineStarts(s); 1021 if (lineStartsOfS.length > 1) { 1022 // 1: The first element of the lineStartsOfS 1023 lineCount = lineCount + lineStartsOfS.length - 1; 1024 linePos = output.length - s.length + last(lineStartsOfS); 1025 lineStart = (linePos - output.length) === 0; 1026 } 1027 else { 1028 lineStart = false; 1029 } 1030 } 1031 1032 function writeText(s: string) { 1033 if (s && s.length) { 1034 if (lineStart) { 1035 lineStart = false; 1036 } 1037 output += s; 1038 updateLineCountAndPosFor(s); 1039 } 1040 } 1041 1042 function write(s: string) { 1043 writeText(s); 1044 } 1045 1046 function reset(): void { 1047 output = ""; 1048 lineStart = true; 1049 linePos = 0; 1050 lineCount = 0; 1051 } 1052 1053 // This method is used to write indentation and line breaks. If the string is blank, the writing is skipped. 1054 // In addition, this method can be called to write comments and code in bundle mode, but obfuscation is not in bundle mode. 1055 function rawWrite(s: string) { 1056 if (s !== undefined) { 1057 if ((lineStart || endsWithNoSpaceTrailingChar(output)) && s.trim().length === 0) { 1058 return; 1059 } 1060 output += s; 1061 updateLineCountAndPosFor(s); 1062 } 1063 } 1064 1065 function writeLiteral(s: string) { 1066 if (s && s.length) { 1067 write(s); 1068 } 1069 } 1070 1071 function writeLine(force?: boolean): void { 1072 if (!force && (lineStart || endsWithNoSpaceTrailingChar(output))) { 1073 return; 1074 } 1075 output += space; 1076 lineStart = false; 1077 } 1078 1079 function endsWithNoSpaceTrailingChar(input: string): boolean { 1080 // Get the last character of a string. 1081 const lastChar: string = input.charAt(input.length - 1); 1082 return noSpaceTrailingChars.has(lastChar); 1083 } 1084 1085 function getTextPosWithWriteLine() { 1086 return lineStart ? output.length : (output.length + space.length); 1087 } 1088 1089 reset(); 1090 1091 return { 1092 write, 1093 rawWrite, 1094 writeLiteral, 1095 writeLine, 1096 increaseIndent: noop, 1097 decreaseIndent: noop, 1098 getIndent: () => 0, 1099 getTextPos: () => output.length, 1100 getLine: () => lineCount, 1101 getColumn: () => lineStart ? 0 : output.length - linePos, 1102 getText: () => output, 1103 isAtStartOfLine: () => lineStart, 1104 hasTrailingComment: () => false, 1105 hasTrailingWhitespace: () => !!output.length && isWhiteSpaceLike(output.charCodeAt(output.length - 1)), 1106 clear: reset, 1107 reportInaccessibleThisError: noop, 1108 reportPrivateInBaseOfClassExpression: noop, 1109 reportInaccessibleUniqueSymbolError: noop, 1110 trackSymbol: () => false, 1111 writeKeyword: write, 1112 writeOperator: write, 1113 writeParameter: write, 1114 writeProperty: write, 1115 writePunctuation: write, 1116 writeSpace: write, 1117 writeStringLiteral: write, 1118 writeSymbol: (s, _) => write(s), 1119 writeTrailingSemicolon: write, 1120 writeComment: noop, 1121 getTextPosWithWriteLine 1122 }; 1123} 1124 1125/** @internal */ 1126export function isSendableFunctionOrType(node: Node, maybeNotOriginalNode: boolean = false): boolean { 1127 if (!node || !(isFunctionDeclaration(node) || isTypeAliasDeclaration(node))) { 1128 return false; 1129 } 1130 if (!isInEtsFile(node) && !(maybeNotOriginalNode && isInEtsFileWithOriginal(node))) { 1131 return false; 1132 } 1133 const illegalDecorators = getIllegalDecorators(node); 1134 if (!illegalDecorators || illegalDecorators.length !== 1) { 1135 return false; 1136 } 1137 const nameExpr = illegalDecorators[0].expression; 1138 return (isIdentifier(nameExpr) && nameExpr.escapedText.toString() === 'Sendable'); 1139} 1140 1141/** @internal */ 1142export function checkStructPropertyPosition(declaration: Node, prop: Node): boolean { 1143 // Struct can't be declared inside another struct, so if the parent of PropertyDeclaration node is StructDeclaration 1144 // and its end position is behind the end position of PropertyDeclaration node, we can make sure the property is declared and used in the same struct. 1145 return declaration.pos < prop.pos && isStructDeclaration(declaration.parent) && declaration.parent.end > prop.end; 1146} 1147 1148export const REQUIRE_DECORATOR = 'Require'; 1149const JSON_SUFFIX = '.json'; 1150const KIT_PREFIX = '@kit.'; 1151const DEFAULT_KEYWORD = 'default'; 1152const ETS_DECLARATION = '.d.ets'; 1153export const THROWS_TAG = 'throws'; 1154export const THROWS_CATCH = 'catch'; 1155export const THROWS_ASYNC_CALLBACK = 'AsyncCallback'; 1156export const THROWS_ERROR_CALLBACK = 'ErrorCallback'; 1157 1158interface KitSymbolInfo { 1159 source: string, 1160 bindings: string, 1161} 1162 1163interface KitJsonInfo { 1164 symbols: { 1165 [symbol: string]: KitSymbolInfo, 1166 } 1167} 1168 1169const kitJsonCache = new Map<string, KitJsonInfo | undefined>(); 1170 1171/** @internal */ 1172export function getSdkPath(compilerOptions: CompilerOptions): string | undefined { 1173 if (isMixedCompilerSDKPath(compilerOptions)) { 1174 return resolvePath(compilerOptions.etsLoaderPath!, '../../../../..'); 1175 } 1176 return compilerOptions.etsLoaderPath ? resolvePath(compilerOptions.etsLoaderPath, '../../../..') : undefined; 1177} 1178 1179function getKitJsonObject(name: string, sdkPath: string, compilerOptions: CompilerOptions): KitJsonInfo | undefined { 1180 if (kitJsonCache?.has(name)) { 1181 return kitJsonCache.get(name); 1182 } 1183 const OHOS_KIT_CONFIG_PATH = isMixedCompilerSDKPath(compilerOptions) ? 1184 './openharmony/ets/ets1.1/build-tools/ets-loader/kit_configs' : 1185 './openharmony/ets/build-tools/ets-loader/kit_configs'; 1186 const HMS_KIT_CONFIG_PATH = isMixedCompilerSDKPath(compilerOptions) ? 1187 './hms/ets/ets1.1/build-tools/ets-loader/kit_configs' : 1188 './hms/ets/build-tools/ets-loader/kit_configs'; 1189 const ohosJsonPath = resolvePath(sdkPath, OHOS_KIT_CONFIG_PATH, `./${name}${JSON_SUFFIX}`); 1190 const hmsJsonPath = resolvePath(sdkPath, HMS_KIT_CONFIG_PATH, `./${name}${JSON_SUFFIX}`); 1191 1192 let fileInfo: string | undefined = 1193 sys.fileExists(ohosJsonPath) ? sys.readFile(ohosJsonPath, 'utf-8') : 1194 sys.fileExists(hmsJsonPath) ? sys.readFile(hmsJsonPath, 'utf-8') : 1195 undefined; 1196 if (!fileInfo) { 1197 kitJsonCache?.set(name, undefined); 1198 return undefined; 1199 } 1200 1201 const obj = JSON.parse(fileInfo) as KitJsonInfo; 1202 kitJsonCache?.set(name, obj); 1203 1204 return obj; 1205} 1206 1207// Determine if it is a 1.2 SDK path 1208export function isMixedCompilerSDKPath(compilerOptions: CompilerOptions): boolean { 1209 if (!compilerOptions.etsLoaderPath) { 1210 return false; 1211 } 1212 if (normalizePath(compilerOptions.etsLoaderPath).endsWith('ets1.1/build-tools/ets-loader')) { 1213 return true; 1214 } 1215 return false; 1216} 1217 1218export function cleanKitJsonCache(): void { 1219 kitJsonCache?.clear(); 1220} 1221 1222function setVirtualNodeAndKitImportFlags<T extends Node>(node: T, start: number = 0, end: number = 0): T { 1223 node.virtual = true; 1224 setTextRangePosEnd(node, start, end); 1225 (node as Mutable<T>).flags |= NodeFlags.KitImportFlags; 1226 return node; 1227} 1228 1229function setNoOriginalText<T extends Node>(node: T): T { 1230 (node as Mutable<T>).flags |= NodeFlags.NoOriginalText; 1231 return node; 1232} 1233 1234function createNameImportDeclaration(factory: NodeFactory, isType: boolean, name: Identifier, source: string, 1235 oldStatement: ImportDeclaration, importSpecifier: TextRange, isLazy?: boolean): ImportDeclaration { 1236 const oldModuleSpecifier = oldStatement.moduleSpecifier; 1237 const newModuleSpecifier = setNoOriginalText(setVirtualNodeAndKitImportFlags( 1238 factory.createStringLiteral(source), oldModuleSpecifier.pos, oldModuleSpecifier.end) 1239 ); 1240 let newImportClause = factory.createImportClause(isType, name, undefined); 1241 // Add the isLazy flag in the original importDeclaration to the new importClause statement. 1242 (newImportClause as Mutable<ImportClause>).isLazy = isLazy; 1243 newImportClause = setVirtualNodeAndKitImportFlags(newImportClause, importSpecifier.pos, importSpecifier.end); 1244 const newImportDeclaration = setVirtualNodeAndKitImportFlags( 1245 factory.createImportDeclaration(undefined, newImportClause, newModuleSpecifier), oldStatement.pos, oldStatement.end); 1246 return newImportDeclaration; 1247} 1248 1249function createBindingImportDeclaration(factory: NodeFactory, isType: boolean, propname: string, name: Identifier, source: string, 1250 oldStatement: ImportDeclaration, importSpecifier: TextRange, isLazy?: boolean): ImportDeclaration { 1251 const oldModuleSpecifier = oldStatement.moduleSpecifier; 1252 const newModuleSpecifier = setNoOriginalText( 1253 setVirtualNodeAndKitImportFlags(factory.createStringLiteral(source), oldModuleSpecifier.pos, oldModuleSpecifier.end)); 1254 const newPropertyName = setNoOriginalText(setVirtualNodeAndKitImportFlags(factory.createIdentifier(propname), name.pos, name.end)); 1255 // The location information of the newImportSpecific is created using the location information of the old importSpecifier. 1256 const newImportSpecific = setVirtualNodeAndKitImportFlags( 1257 factory.createImportSpecifier(false, newPropertyName, name), importSpecifier.pos, importSpecifier.end); 1258 // The location information of the newNamedBindings is created using the location information of the old importSpecifier. 1259 const newNamedBindings = setVirtualNodeAndKitImportFlags(factory.createNamedImports([newImportSpecific]), importSpecifier.pos, importSpecifier.end); 1260 let newImportClause = factory.createImportClause(isType, undefined, newNamedBindings); 1261 // Add the isLazy flag in the original importDeclaration to the new importClause statement. 1262 (newImportClause as Mutable<ImportClause>).isLazy = isLazy; 1263 // The location information of the newImportClause is created using the location information of the old importSpecifier. 1264 newImportClause = setVirtualNodeAndKitImportFlags( 1265 newImportClause, importSpecifier.pos, importSpecifier.end); 1266 const newImportDeclaration = setVirtualNodeAndKitImportFlags( 1267 factory.createImportDeclaration(undefined, newImportClause, newModuleSpecifier), oldStatement.pos, oldStatement.end); 1268 return newImportDeclaration; 1269} 1270 1271function createImportDeclarationForKit(factory: NodeFactory, isType: boolean, name: Identifier, symbol: KitSymbolInfo, 1272 oldStatement: ImportDeclaration, importSpecifier: TextRange, isLazy?: boolean): ImportDeclaration { 1273 const source = symbol.source.replace(/\.d.[e]?ts$/, ''); 1274 const binding = symbol.bindings; 1275 if (binding === DEFAULT_KEYWORD) { 1276 return createNameImportDeclaration(factory, isType, name, source, oldStatement, importSpecifier, isLazy); 1277 } 1278 return createBindingImportDeclaration(factory, isType, binding, name, source, oldStatement, importSpecifier, isLazy); 1279} 1280 1281function markKitImport(statement : Statement, markedkitImportRanges: Array<TextRange>): void { 1282 markedkitImportRanges.push({ pos: statement.pos, end: statement.end }); 1283} 1284 1285/** @internal */ 1286export function isInMarkedKitImport(sourceFile: SourceFile, pos: number, end: number): boolean { 1287 return !!sourceFile.markedKitImportRange?.some( 1288 range => { 1289 return (range.pos <= pos) && (end <= range.end); 1290 } 1291 ); 1292} 1293 1294function excludeStatementForKitImport(statement: Statement): boolean { 1295 if (!isImportDeclaration(statement) || // check is ImportDeclaration 1296 !statement.importClause || // exclude import 'mode' 1297 (statement.importClause.namedBindings && isNamespaceImport(statement.importClause.namedBindings)) || // exclude namespace import 1298 !isStringLiteral(statement.moduleSpecifier) || statement.illegalDecorators || // exclude if may has error 1299 !statement.moduleSpecifier.text.startsWith(KIT_PREFIX) || // is not kit import 1300 statement.modifiers || // exclude if has modifiers 1301 statement.assertClause) { // not support assertClause 1302 return true; 1303 } 1304 return false; 1305} 1306 1307interface WhiteListInfo { 1308 kitName: string; 1309 symbolName: string; 1310} 1311// This symbols have error in kit files, so add it in white list and don't change 1312const whiteListForErrorSymbol: WhiteListInfo[] = [ 1313 { kitName: '@kit.CoreFileKit', symbolName: 'DfsListeners' }, 1314 { kitName: '@kit.NetworkKit', symbolName: 'VpnExtensionContext' }, 1315 { kitName: '@kit.ArkUI', symbolName: 'CustomContentDialog' }, 1316]; 1317 1318// This symbol will clause new warning in ts files 1319const whiteListForTsWarning: WhiteListInfo[] = [ 1320 { kitName: '@kit.ConnectivityKit', symbolName: 'socket' }, 1321]; 1322 1323// This files import the symbol from ets file, so we won't change them in ts files 1324const whiteListForTsFile: Set<string> = new Set([ 1325 '@kit.AccountKit', '@kit.MapKit', '@kit.Penkit', '@kit.ScenarioFusionKit', 1326 '@kit.ServiceCollaborationKit', '@kit.SpeechKit', '@kit.VisionKit', 1327 '@kit.PDFKit', '@kit.ReaderKit', 1328]); 1329 1330function inWhiteList(moduleSpecifierText: string, importName: string, inEtsContext: boolean): boolean { 1331 if (whiteListForErrorSymbol.some(info => (info.kitName === moduleSpecifierText && info.symbolName === importName))) { 1332 return true; 1333 } 1334 if (!inEtsContext && 1335 whiteListForTsWarning.some(info => (info.kitName === moduleSpecifierText && info.symbolName === importName))) { 1336 return true; 1337 } 1338 return false; 1339} 1340 1341function processKitStatementSuccess(factory: NodeFactory, statement: ImportDeclaration, jsonObject: KitJsonInfo | undefined, inEtsContext: boolean, 1342 newImportStatements: Array<ImportDeclaration>): boolean { 1343 const importClause = statement.importClause!; 1344 const moduleSpecifierText = (statement.moduleSpecifier as StringLiteral).text; 1345 const kitSymbol = jsonObject?.symbols; 1346 if (!kitSymbol) { 1347 return false; 1348 } 1349 1350 const isType = importClause.isTypeOnly; 1351 const isLazy = importClause.isLazy; 1352 if (importClause.name) { 1353 const symbol = kitSymbol[DEFAULT_KEYWORD]; 1354 // has error when import ets declaration in ts file 1355 if (!symbol || (!inEtsContext && symbol.source.endsWith(ETS_DECLARATION))) { 1356 return false; 1357 } 1358 newImportStatements.push(createImportDeclarationForKit(factory, isType, importClause.name, symbol, statement, importClause.name, isLazy)); 1359 } 1360 1361 if (importClause.namedBindings) { 1362 let hasError = false; 1363 (importClause.namedBindings as NamedImports).elements.forEach( 1364 element => { 1365 if (hasError) { 1366 return; 1367 } 1368 const importName = unescapeLeadingUnderscores(element.propertyName ? element.propertyName.escapedText : element.name.escapedText); 1369 const aliasName = element.name; 1370 1371 if (inWhiteList(moduleSpecifierText, importName, inEtsContext)) { 1372 hasError = true; 1373 return; 1374 } 1375 1376 const symbol = kitSymbol[importName]; 1377 if (!symbol || !aliasName || 1378 // has error when import ets declaration in ts file 1379 (!inEtsContext && symbol.source.endsWith(ETS_DECLARATION)) || 1380 // can not have duplicate type 1381 (isType && element.isTypeOnly)) { 1382 hasError = true; 1383 return; 1384 } 1385 1386 newImportStatements.push( 1387 createImportDeclarationForKit(factory, isType || element.isTypeOnly, aliasName, symbol, statement, element, isLazy)); 1388 } 1389 ); 1390 if (hasError) { 1391 return false; 1392 } 1393 } 1394 return true; 1395} 1396 1397/** @internal */ 1398export function processKit(factory: NodeFactory, statements: NodeArray<Statement>, sdkPath: string, 1399 markedkitImportRanges: Array<TextRange>, inEtsContext: boolean, compilerOptions: CompilerOptions): Statement[] { 1400 const list: Statement[] = []; 1401 let skipRestStatements = false; 1402 statements.forEach( 1403 statement => { 1404 // ArkTS don't allow import declaration after other statements 1405 if (!skipRestStatements && inEtsContext && !isImportDeclaration(statement)) { 1406 skipRestStatements = true; 1407 } 1408 if (skipRestStatements || excludeStatementForKitImport(statement)) { 1409 list.push(statement); 1410 return; 1411 } 1412 1413 const moduleSpecifierText = ((statement as ImportDeclaration).moduleSpecifier as StringLiteral).text; 1414 if (!inEtsContext && whiteListForTsFile.has(moduleSpecifierText)) { 1415 list.push(statement); 1416 return; 1417 } 1418 1419 const jsonObject = getKitJsonObject(moduleSpecifierText, sdkPath, compilerOptions); 1420 const newImportStatements = new Array<ImportDeclaration>(); 1421 1422 if (!processKitStatementSuccess(factory, statement as ImportDeclaration, jsonObject, inEtsContext, newImportStatements)) { 1423 list.push(statement); 1424 return; 1425 } 1426 1427 list.push(...newImportStatements); 1428 markKitImport(statement, markedkitImportRanges); 1429 } 1430 ); 1431 return list; 1432} 1433 1434export function getMaxFlowDepth(compilerOptions: CompilerOptions): number { 1435 // The value of maxFlowDepth ranges from 2000 to 65535. 1436 return compilerOptions.maxFlowDepth || maxFlowDepthDefaultValue; 1437} 1438 1439export interface MoreInfo { 1440 cn: string, 1441 en: string 1442} 1443 1444export class ErrorInfo { 1445 code: string = ''; 1446 description: string = 'ArkTS Compiler Error'; // The description of type errors for TSC 1447 cause: string = ''; 1448 position: string = ''; 1449 solutions: string[] = []; 1450 moreInfo?: MoreInfo; 1451 1452 getCode(): string { 1453 return this.code; 1454 } 1455 1456 getDescription(): string { 1457 return this.description; 1458 } 1459 1460 getCause(): string { 1461 return this.cause; 1462 } 1463 1464 getPosition(): string { 1465 return this.position; 1466 } 1467 1468 getSolutions(): string[] { 1469 return this.solutions; 1470 } 1471 1472 getMoreInfo(): MoreInfo | undefined { 1473 return this.moreInfo; 1474 } 1475} 1476 1477export enum ErrorCodeArea { 1478 TSC = 0, 1479 LINTER = 1, 1480 UI = 2 1481} 1482 1483const SUBSYSTEM_CODE = '105'; // Subsystem coding 1484const ERROR_TYPE_CODE = '05'; // Error type code 1485const EXTENSION_CODE = '001'; // Extended codes defined by various subsystems 1486const codeCollectionUI = new Set([28000, 28001, 28002, 28003, 28004, 28005, 28006, 28007, 28015]); // UI code error collection 1487const codeCollectionLinter = new Set([28016, 28017]); // Linter code error collection 1488const newTscCodeMap = new Map([ 1489 [28014, '10505114'] 1490]); // New tsc code error collection 1491 1492// Currently, only the tsc error reporting triggers this function. 1493export function getErrorCode(diagnostic: Diagnostic): ErrorInfo { 1494 let errorInfo: ErrorInfo = new ErrorInfo(); 1495 errorInfo.cause = flattenDiagnosticMessageText(diagnostic.messageText, '\n'); 1496 1497 if (diagnostic.file) { 1498 const { line, character }: LineAndCharacter = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!); 1499 errorInfo.position = `File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}`; 1500 } 1501 1502 if (newTscCodeMap.has(diagnostic.code)) { 1503 errorInfo.code = newTscCodeMap.get(diagnostic.code) as string; 1504 } else { 1505 errorInfo.code = SUBSYSTEM_CODE + ERROR_TYPE_CODE + EXTENSION_CODE; 1506 } 1507 return errorInfo; 1508} 1509 1510export function getErrorCodeArea(code: number): ErrorCodeArea { 1511 if (codeCollectionLinter.has(code)) { 1512 return ErrorCodeArea.LINTER; 1513 } else if (codeCollectionUI.has(code)) { 1514 return ErrorCodeArea.UI; 1515 } else { 1516 return ErrorCodeArea.TSC; 1517 } 1518}