• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}