From 28c4ccbb54f99023d631b353034ed6fb771e5d05 Mon Sep 17 00:00:00 2001 From: xuhangqi Date: Sat, 14 Jun 2025 22:20:52 +0800 Subject: [PATCH] Transform import kit for sdk interop Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/IC9OXV Signed-off-by: xuhangqi Change-Id: Ic17116b14e519fc19a4235e66ca55b72aea806ec --- ets2panda/driver/build_system/README.md | 4 +- ets2panda/driver/build_system/package.json | 1 + .../build_system/src/build/base_mode.ts | 11 +- .../src/build/compile_thread_worker.ts | 8 +- .../build_system/src/build/compile_worker.ts | 7 + .../src/build/generate_arktsconfig.ts | 86 +++++++- .../driver/build_system/src/error_code.ts | 1 + .../src/plugins/KitImportTransformer.ts | 187 ++++++++++++++++++ .../driver/build_system/src/pre_define.ts | 1 + ets2panda/driver/build_system/src/types.ts | 49 +++++ .../test/plugin/@kit.abilityKit.json | 10 + .../test/plugin/KitImportTransformer.test.ts | 131 ++++++++++++ 12 files changed, 486 insertions(+), 10 deletions(-) create mode 100644 ets2panda/driver/build_system/src/plugins/KitImportTransformer.ts create mode 100644 ets2panda/driver/build_system/test/plugin/@kit.abilityKit.json create mode 100644 ets2panda/driver/build_system/test/plugin/KitImportTransformer.test.ts diff --git a/ets2panda/driver/build_system/README.md b/ets2panda/driver/build_system/README.md index d685a1014e..414e8efc04 100644 --- a/ets2panda/driver/build_system/README.md +++ b/ets2panda/driver/build_system/README.md @@ -56,11 +56,13 @@ Run tests (using Jest) Jest is almost ready to use out of the box. The test file path has been configured through jest.config.js. In the build_system/test directory, scan for test files with the suffix .test.ts -Install Jest and its TypeScript support libraries +Install Jest and its TypeScript support libraries. +If execute npm run test:all, must first install npm-run-all, and enabling concurrency is a necessary condition ```bash npm install --save-dev jest ts-jest @types/jest ``` To run tests: ```bash npm run ut_test +npm run plugin_test ``` \ No newline at end of file diff --git a/ets2panda/driver/build_system/package.json b/ets2panda/driver/build_system/package.json index c4259b39b8..92db8a38ca 100644 --- a/ets2panda/driver/build_system/package.json +++ b/ets2panda/driver/build_system/package.json @@ -41,6 +41,7 @@ "demo_hap:gen_abc": "npm run build && node ./dist/entry.js test/demo_hap/build_config.json", "ut_test": "jest test/ut", + "plugin_test": "jest --testMatch='/test/plugin/**/*.test.ts'", "build_system_Utest": "jest --testMatch='**/test/ut/**/*.test.ts' --testPathIgnorePatterns='test/e2e/'", "build_system_Etest": "TEST=demo_entry1.2_hsp1.1:gen_abc jest --testMatch='**/test/e2e/*.test.ts' --testPathIgnorePatterns='test/ut/'" }, diff --git a/ets2panda/driver/build_system/src/build/base_mode.ts b/ets2panda/driver/build_system/src/build/base_mode.ts index 50983025a8..2327bc2029 100644 --- a/ets2panda/driver/build_system/src/build/base_mode.ts +++ b/ets2panda/driver/build_system/src/build/base_mode.ts @@ -69,7 +69,7 @@ import { } from '../types'; import { ArkTSConfigGenerator } from './generate_arktsconfig'; import { SetupClusterOptions } from '../types'; - +import { KitImportTransformer } from '../plugins/KitImportTransformer'; export abstract class BaseMode { public buildConfig: BuildConfig; public entryFiles: Set; @@ -241,7 +241,14 @@ export abstract class BaseMode { arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); this.logger.printInfo('es2panda proceedToState parsed'); let ast = arkts.EtsScript.fromContext(); - PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + if (this.buildConfig.aliasConfig?.size > 0) { + // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin + this.logger.printInfo('Transforming import statements with alias config'); + let transformAst = new KitImportTransformer(arkts, arktsGlobal.compilerContext.program, this.buildConfig.buildSdkPath,this.buildConfig.aliasConfig).transform(ast); + PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); + } else { + PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); + } PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); this.logger.printInfo('plugin parsed finished'); diff --git a/ets2panda/driver/build_system/src/build/compile_thread_worker.ts b/ets2panda/driver/build_system/src/build/compile_thread_worker.ts index 8eb6212e58..987221aaf9 100644 --- a/ets2panda/driver/build_system/src/build/compile_thread_worker.ts +++ b/ets2panda/driver/build_system/src/build/compile_thread_worker.ts @@ -37,6 +37,7 @@ import { Logger } from '../logger'; import { ErrorCode } from '../error_code'; +import { KitImportTransformer } from '../plugins/KitImportTransformer'; const { workerId } = workerData; @@ -72,7 +73,12 @@ function compileAbc(jobInfo: JobInfo): void { PluginDriver.getInstance().getPluginContext().setContextPtr(context); arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context); - + if (config.aliasConfig?.size > 0) { + // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin + let ast = arkts.EtsScript.fromContext(); + let transformAst = new KitImportTransformer(arkts, arktsGlobal.compilerContext.program, config.buildSdkPath, config.aliasConfig).transform(ast); + PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); + } PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context); diff --git a/ets2panda/driver/build_system/src/build/compile_worker.ts b/ets2panda/driver/build_system/src/build/compile_worker.ts index c98142c648..d6eb4dcad3 100644 --- a/ets2panda/driver/build_system/src/build/compile_worker.ts +++ b/ets2panda/driver/build_system/src/build/compile_worker.ts @@ -52,6 +52,7 @@ process.on('message', (message: { PluginDriver.getInstance().initPlugins(buildConfig); const koalaWrapperPath = process.env.KOALA_WRAPPER_PATH ?? path.resolve(buildConfig.buildSdkPath, KOALA_WRAPPER_PATH_FROM_SDK); let { arkts, arktsGlobal } = require(koalaWrapperPath); + const { KitImportTransformer } = require("../plugins/KitImportTransformer"); for (const fileInfo of taskList) { let errorStatus = false; @@ -76,6 +77,12 @@ process.on('message', (message: { PluginDriver.getInstance().getPluginContext().setArkTSProgram(arktsGlobal.compilerContext.program); arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); + if (buildConfig.aliasConfig?.size > 0) { + // if aliasConfig is set, transform aliasName@kit.xxx to default@ohos.xxx through the plugin + let ast = arkts.EtsScript.fromContext(); + let transformAst = new KitImportTransformer(arkts, arktsGlobal.compilerContext.program, buildConfig.buildSdkPath, buildConfig.aliasConfig).transform(ast); + PluginDriver.getInstance().getPluginContext().setArkTSAst(transformAst); + } PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer); diff --git a/ets2panda/driver/build_system/src/build/generate_arktsconfig.ts b/ets2panda/driver/build_system/src/build/generate_arktsconfig.ts index 0e799bda50..2d9caf7d1d 100644 --- a/ets2panda/driver/build_system/src/build/generate_arktsconfig.ts +++ b/ets2panda/driver/build_system/src/build/generate_arktsconfig.ts @@ -286,14 +286,88 @@ export class ArkTSConfigGenerator { private processAlias(moduleInfo: ModuleInfo, dynamicPathSection: Record): void { const aliasForPkg: Map | undefined = this.aliasConfig?.get(moduleInfo.packageName); + aliasForPkg?.forEach((aliasConfig, aliasName) => { - // treat kit as 1.2,then do kit transform in plugin and reparse - if (aliasConfig.isStatic || aliasConfig.originalAPIName.startsWith('@kit')) { - this.processStaticAlias(aliasName, aliasConfig); - } else { - this.processDynamicAlias(aliasName, aliasConfig, dynamicPathSection); + if (aliasConfig.isStatic || aliasConfig.originalAPIName.startsWith('@kit')) { + this.processStaticAlias(aliasName, aliasConfig); + } else { + this.processDynamicAlias(aliasName, aliasConfig, dynamicPathSection); + } + }); + + this.dynamicSDKPaths.forEach(basePath => { + if (fs.existsSync(basePath)) { + this.traverseDynamicPath(basePath, '', false, dynamicPathSection); + } else { + this.logger.printWarn(`sdk path ${basePath} not exist.`); + } + }); + } + + private traverseDynamicPath( + currentDir: string, + relativePath: string, + isExcludedDir: boolean, + dynamicPathSection: Record, + allowedExtensions: string[] = ['.d.ets'] + ): void { + const items = fs.readdirSync(currentDir); + + for (const item of items) { + const itemPath = path.join(currentDir, item); + const stat = fs.statSync(itemPath); + + if (stat.isFile()) { + if (this.isAllowedExtension(item, allowedExtensions)) { + this.processDynamicFile(itemPath, item, relativePath, isExcludedDir, dynamicPathSection); + } + continue; + } + + if (stat.isDirectory()) { + const isRuntimeAPI = path.basename(currentDir) === 'arkui' && item === 'runtime-api'; + const newRelativePath = isRuntimeAPI ? '' : (relativePath ? `${relativePath}.${item}` : item); + this.traverseDynamicPath( + path.resolve(currentDir, item), + newRelativePath, + isExcludedDir || isRuntimeAPI, + dynamicPathSection, + allowedExtensions + ); + } } - }) + } + + private isAllowedExtension(fileName: string, allowedExtensions: string[]): boolean { + return allowedExtensions.some(ext => fileName.endsWith(ext)); + } + + private isValidAPIFile(fileName: string): boolean { + const pattern = new RegExp(`^@(${sdkConfigPrefix})\\..+\.d\.ets$`, 'i'); + return pattern.test(fileName); + } + + private buildDynamicKey(baseName: string, relativePath: string, isExcludedDir: boolean): string { + return 'default' + (isExcludedDir ? baseName : (relativePath ? `${relativePath}.${baseName}` : baseName)); + } + + private processDynamicFile( + filePath: string, + fileName: string, + relativePath: string, + isExcludedDir: boolean, + dynamicPathSection: Record + ): void { + if (!this.isValidAPIFile(fileName)) return; + + const baseName = path.basename(fileName, '.d.ets'); + const key = this.buildDynamicKey(baseName, relativePath, isExcludedDir); + + dynamicPathSection[key] = { + language: 'js', + declPath: filePath, + ohmUrl: getOhmurlByApi(baseName) + }; } private processStaticAlias(aliasName: string, aliasConfig: AliasConfig) { diff --git a/ets2panda/driver/build_system/src/error_code.ts b/ets2panda/driver/build_system/src/error_code.ts index 10e0c3f471..4271e4086b 100644 --- a/ets2panda/driver/build_system/src/error_code.ts +++ b/ets2panda/driver/build_system/src/error_code.ts @@ -40,4 +40,5 @@ export enum ErrorCode { BUILDSYSTEM_PATH_RESOLVE_FAIL = '11410019', BUILDSYSTEM_INTEROP_SDK_NOT_FIND = '11410020', BUILDSYSTEM_INIT_ALIAS_CONFIG_FAILED = '11410021', + BUILDSYSTEM_PLUGIN_ALIAS_CONFIG_PARSING_FAIL = '11410022', } diff --git a/ets2panda/driver/build_system/src/plugins/KitImportTransformer.ts b/ets2panda/driver/build_system/src/plugins/KitImportTransformer.ts new file mode 100644 index 0000000000..0f0f1ad077 --- /dev/null +++ b/ets2panda/driver/build_system/src/plugins/KitImportTransformer.ts @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import { AliasConfig, ArkTS } from '../types'; +import { + Logger, + LogData, + LogDataFactory +} from '../logger'; +import { ErrorCode } from '../error_code'; +import { KIT_CONFIGS_PATH_FROM_SDK } from '../pre_define'; + +export class KitImportTransformer { + + private arkts: ArkTS; + private extraImports: ArkTS["ETSImportDeclaration"][] = []; + private sdkAliasConfig: Map>; + private buildSdkPath: string; + private program: object; + private logger: Logger; + + constructor(arkts: ArkTS, program: object, buildSdkPath: string, aliasMap: Map>) { + this.arkts = arkts; + this.buildSdkPath = buildSdkPath; + this.sdkAliasConfig = aliasMap; + this.program = program; + this.logger = Logger.getInstance(); + } + + public transform(astNode: ArkTS["AstNode"]): ArkTS["AstNode"] { + if (!this.arkts.isEtsScript(astNode)) { + return astNode; + } + + const newStatements: ArkTS["AstNode"][] = []; + const dynamicAliasNames = new Set(this.getDynamicAliasNames()); + if (astNode.statements.length === 0) { + return astNode; + } + for (const stmt of astNode.statements) { + if (this.arkts.isETSImportDeclaration(stmt) && dynamicAliasNames.has(stmt.source?.str)) { + this.splitKitImport(stmt); + continue; + } + newStatements.push(stmt); + } + + const finalStatements = [...this.extraImports, ...newStatements]; + + return this.arkts.factory.updateEtsScript(astNode, finalStatements); + } + + private splitKitImport(importNode: ArkTS["ETSImportDeclaration"]): void { + const kitName = importNode.source.str; + const symbolsJson = this.loadKitSymbolsJson(kitName); + if (!symbolsJson) { + return; + } + + const groupedSymbols = this.groupImportSpecifiersBySource(importNode, symbolsJson, kitName); + this.generateSplitImportDeclarations(groupedSymbols); + } + + private loadKitSymbolsJson(kitName: string): any | null { + let jsonFileName: string = this.getOriginalNameByAlias(kitName); + if (jsonFileName === '') { + this.logger.printError(LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_PLUGIN_ALIAS_CONFIG_PARSING_FAIL, + `json file: '${jsonFileName}' not found in kit config contents` + )); + return null; + } + const configPath = path.resolve(this.buildSdkPath, KIT_CONFIGS_PATH_FROM_SDK, `${jsonFileName}.json`); + + if (!fs.existsSync(configPath)) { + this.logger.printError(LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_PLUGIN_ALIAS_CONFIG_PARSING_FAIL, + `Kit config file not found for ${kitName}`, + configPath + )); + return null; + } + + try { + return JSON.parse(fs.readFileSync(configPath, 'utf-8')); + } catch (error) { + this.logger.printError(LogDataFactory.newInstance( + ErrorCode.BUILDSYSTEM_PLUGIN_ALIAS_CONFIG_PARSING_FAIL, + `Failed to parse kit config JSON for ${kitName}`, + error instanceof Error ? error.message : String(error) + )); + return null; + } + } + + private groupImportSpecifiersBySource(importNode: ArkTS["ETSImportDeclaration"], symbolsJson: any, kitName: string): Map { + const grouped = new Map(); + + for (const specifier of importNode.specifiers) { + if (!this.arkts.isImportSpecifier(specifier)) { + continue; + } + + const symbolName = specifier.imported?.name; + if (!symbolName) { + continue; + } + + const symbolEntry = symbolsJson.symbols?.[symbolName]; + if (!symbolEntry?.source) { + this.logger.printWarn(`Symbol '${symbolName}' not found in ${kitName}.json`); + continue; + } + + const sourcePath = "default" + symbolEntry.source.replace(/\.d\.ts$/, ''); + if (!grouped.has(sourcePath)) { + grouped.set(sourcePath, []); + } + grouped.get(sourcePath)!.push(symbolName); + } + + return grouped; + } + + private generateSplitImportDeclarations(groupedSymbols: Map): void { + for (const [source, names] of groupedSymbols.entries()) { + const specifiers = names.map(name => + this.arkts.factory.createImportSpecifier( + this.arkts.factory.createIdentifier(name), + this.arkts.factory.createIdentifier(name) + ) + ); + + const importDecl = this.arkts.factory.createImportDeclaration( + this.arkts.factory.createStringLiteral(source), + specifiers, + this.arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE, + this.program, + this.arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE + ); + this.extraImports.push(importDecl); + } + } + + private getDynamicAliasNames(): Set { + const dynamicAliasNames = new Set(); + + if (this.sdkAliasConfig.size === 0) { + return dynamicAliasNames; + } + + for (const innerMap of this.sdkAliasConfig.values()) { + for (const [aliasName, aliasConfig] of innerMap.entries()) { + if (!aliasConfig.originalAPIName.startsWith('@kit')) { + continue; + } + if (!aliasConfig.isStatic) { + dynamicAliasNames.add(aliasName); + } + } + } + return dynamicAliasNames; + } + + private getOriginalNameByAlias(aliasName: string): string { + for (const innerMap of this.sdkAliasConfig.values()) { + if (innerMap.has(aliasName)) { + return innerMap.get(aliasName)!.originalAPIName; + } + } + return ''; + } +} diff --git a/ets2panda/driver/build_system/src/pre_define.ts b/ets2panda/driver/build_system/src/pre_define.ts index fa357a9e53..3879de18ee 100644 --- a/ets2panda/driver/build_system/src/pre_define.ts +++ b/ets2panda/driver/build_system/src/pre_define.ts @@ -34,6 +34,7 @@ export enum LANGUAGE_VERSION { export const PANDA_SDK_PATH_FROM_SDK: string = './build-tools/ets2panda'; export const SYSTEM_SDK_PATH_FROM_SDK: string = './'; export const KOALA_WRAPPER_PATH_FROM_SDK: string = './build-tools/koala-wrapper/build/lib/es2panda'; +export const KIT_CONFIGS_PATH_FROM_SDK: string = '../ets1.1/build-tools/ets-loader/kit_configs'; export const DEFAULT_WOKER_NUMS: number = 4; diff --git a/ets2panda/driver/build_system/src/types.ts b/ets2panda/driver/build_system/src/types.ts index 84dd9b5082..e4ea3db76e 100644 --- a/ets2panda/driver/build_system/src/types.ts +++ b/ets2panda/driver/build_system/src/types.ts @@ -79,6 +79,22 @@ export interface ArkTS { Es2pandaContextState: typeof Es2pandaContextState; MemInitialize: Function; CreateGlobalContext: Function; + AstNode: AstNode; + ETSImportDeclaration: ETSImportDeclaration; + isEtsScript: Function; + isImportSpecifier: Function; + isETSImportDeclaration: Function; + factory: { + createEtsScript: Function; + createImportDeclaration: Function; + createImportSpecifier: Function; + createLiteral: Function; + createIdentifier: Function; + updateEtsScript: Function; + createStringLiteral: Function; + }; + Es2pandaImportKinds: typeof Es2pandaImportKinds; + Es2pandaImportFlags: typeof Es2pandaImportFlags; } export enum Es2pandaContextState { @@ -232,4 +248,37 @@ export type KPointer = number | bigint; export interface AliasConfig { originalAPIName: string; isStatic: boolean; +} + +export interface AstNode { + kind: string; + statements: AstNode[]; + source: LiteralNode; + specifiers: ImportSpecifierNode[]; +} + +export interface LiteralNode { + str: string; + clone: Function; +} + +export interface IdentifierNode { + name: string; +} + +export interface ImportSpecifierNode { + imported?: IdentifierNode; +} + +export interface ETSImportDeclaration extends AstNode { + specifiers: ImportSpecifierNode[]; + source: LiteralNode; +} + +export enum Es2pandaImportKinds { + IMPORT_KINDS_VALUE = 0, +} + +export enum Es2pandaImportFlags { + IMPORT_FLAGS_NONE, } \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/plugin/@kit.abilityKit.json b/ets2panda/driver/build_system/test/plugin/@kit.abilityKit.json new file mode 100644 index 0000000000..4e2d1bfa7e --- /dev/null +++ b/ets2panda/driver/build_system/test/plugin/@kit.abilityKit.json @@ -0,0 +1,10 @@ +{ + "symbols": { + "foo": { + "source": "@ohos.app.ability.common.d.ts" + }, + "bar": { + "source": "@ohos.app.ability.ConfigurationConstant.d.ts" + } + } +} \ No newline at end of file diff --git a/ets2panda/driver/build_system/test/plugin/KitImportTransformer.test.ts b/ets2panda/driver/build_system/test/plugin/KitImportTransformer.test.ts new file mode 100644 index 0000000000..6fa6dc7ce6 --- /dev/null +++ b/ets2panda/driver/build_system/test/plugin/KitImportTransformer.test.ts @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { AliasConfig, ArkTS } from '../../src/types'; +jest.mock('../../src/logger', () => ({ + Logger: { + getInstance: jest.fn(() => ({ + log: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + })), + }, + })); +jest.mock('../../src/pre_define', () => ({ + KIT_CONFIGS_PATH_FROM_SDK: './plugin' +})); +import { KitImportTransformer } from '../../src/plugins/KitImportTransformer'; + +export const arktsMock: Partial = { + isEtsScript: jest.fn((node) => true), + isETSImportDeclaration: jest.fn((node) => true), + isImportSpecifier: jest.fn(() => true), + factory: { + createImportSpecifier: jest.fn((id) => ({ imported: id })), + createIdentifier: jest.fn((name) => ({ name })), + createLiteral: jest.fn((str) => ({ str })), + createImportDeclaration: jest.fn((source, specifiers) => ({ + kind: 'ImportDeclaration', + source, + specifiers, + })), + updateEtsScript: jest.fn((oldNode, newStatements) => ({ + ...oldNode, + statements: newStatements, + })), + createEtsScript: jest.fn(() => ({ kind: 'EtsScript', statements: [] })), + createStringLiteral: jest.fn((s) => ({ str: s })), + }, + Es2pandaImportKinds: { + IMPORT_KINDS_VALUE: 0, + }, + Es2pandaImportFlags: { + IMPORT_FLAGS_NONE: 0, + } + }; + +const mockAliasConfig: Map> = new Map([ + ['har', new Map([ + ['dynamic@kit.abilityKit', { isStatic: false, originalAPIName: '@kit.abilityKit' }], + ])], +]); + +const mockProgram = {}; + +describe('KitImportTransformer', () => { + + it('should transform kit import to default@ohos.* imports', () => { + const transformer = new KitImportTransformer(arktsMock as ArkTS, mockProgram,'./test', mockAliasConfig); + + const astInput = { + statements: [ + { + source: { str: 'dynamic@kit.abilityKit' }, + specifiers: [ + { imported: { name: 'foo' } }, + { imported: { name: 'bar' } } + ], + }, + ], + }; + + const result = transformer.transform(astInput as any); + + expect(result.statements).toContainEqual({ + kind: 'ImportDeclaration', + source: { str: 'default@ohos.app.ability.common' }, + specifiers: [{ imported: { name: 'foo' } }] + }); + + expect(result.statements).toContainEqual({ + kind: 'ImportDeclaration', + source: { str: 'default@ohos.app.ability.ConfigurationConstant' }, + specifiers: [{ imported: { name: 'bar' } }] + }); + }); + + it('should not transform non-kit imports', () => { + const transformer = new KitImportTransformer(arktsMock as ArkTS, mockProgram, './test', mockAliasConfig); + + const astInput = { + statements: [ + { + source: { str: '@ohos.hilog' }, + specifiers: [{ imported: { name: 'hilog' } }], + }, + ], + }; + + const result = transformer.transform(astInput as any); + expect(result.statements).toEqual(astInput.statements); + }); + + it('should skip unknown kit imports', () => { + const transformer = new KitImportTransformer(arktsMock as ArkTS, mockProgram, './test', mockAliasConfig); + + const astInput = { + statements: [ + { + source: { str: '@kit.unknownKit' }, + specifiers: [{ imported: { name: 'unknown' } }], + }, + ], + }; + + const result = transformer.transform(astInput as any); + expect(result.statements).toEqual(astInput.statements); + }); +}); -- Gitee