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