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