diff --git a/.gitee/CODEOWNERS b/.gitee/CODEOWNERS index 53ea029d5d59d1d069da6eee19412add32963d72..281eab68d9b64f78de571883239f185323e687b2 100755 --- a/.gitee/CODEOWNERS +++ b/.gitee/CODEOWNERS @@ -26,7 +26,7 @@ compiler/test/ut/ @lixingchi1 compiler/test/utForPartialUpdate/ @lixingchi1 compiler/test/utForValidate/ @lixingchi1 compiler/test/transform_ut/ @lixingchi1 -koala-wrapper/ @keerecles +koala-wrapper/ @VictorS67 # Code owners for ArkCompiler ace_ets2bundle diff --git a/arkui-plugins/interop-plugins/index.ts b/arkui-plugins/interop-plugins/index.ts index 404892c3421d7bd8c181f910044cb00309512818..841f74e11e37f16fd0a9e451c04a578d3ca4309f 100644 --- a/arkui-plugins/interop-plugins/index.ts +++ b/arkui-plugins/interop-plugins/index.ts @@ -28,7 +28,7 @@ export function interopTransform():Plugins { name: 'interop-plugin', parsed: parsedTransform, checked: checkedTransform, - clean() { + clean(): void { arkts.arktsGlobal.clearContext(); }, }; diff --git a/arkui-plugins/memo-plugins/parameter-transformer.ts b/arkui-plugins/memo-plugins/parameter-transformer.ts index 061e5b80b82944bbea6835b2883be3e88afa0174..050333da7749d6da1d5fb8aca1a31c7f47fc85e8 100644 --- a/arkui-plugins/memo-plugins/parameter-transformer.ts +++ b/arkui-plugins/memo-plugins/parameter-transformer.ts @@ -235,24 +235,23 @@ export class ParameterTransformer extends AbstractVisitor { } private updateVariableReDeclarationFromParam(node: arkts.VariableDeclaration): arkts.VariableDeclaration { - const that = this; return arkts.factory.updateVariableDeclaration( node, node.modifiers, node.declarationKind, node.declarators.map((declarator) => { - if (that.rewriteMemoInfos?.has(declarator.name.originalPeer)) { - const memoInfo = that.rewriteMemoInfos.get(declarator.name.originalPeer)!; - return that.updateParamReDeclare(declarator, memoInfo); + if (this.rewriteMemoInfos?.has(declarator.name.originalPeer)) { + const memoInfo = this.rewriteMemoInfos.get(declarator.name.originalPeer)!; + return this.updateParamReDeclare(declarator, memoInfo); } if (!!declarator.initializer && arkts.isIdentifier(declarator.initializer)) { const decl = arkts.getPeerDecl(declarator.initializer.originalPeer); - if (decl && that.rewriteIdentifiers?.has(decl.peer)) { + if (decl && this.rewriteIdentifiers?.has(decl.peer)) { return arkts.factory.updateVariableDeclarator( declarator, declarator.flag, declarator.name, - that.rewriteIdentifiers.get(decl.peer)!() + this.rewriteIdentifiers.get(decl.peer)!() ); } } diff --git a/arkui-plugins/ui-plugins/property-translators/utils.ts b/arkui-plugins/ui-plugins/property-translators/utils.ts index 9feb4c4b23808f25f8186e4fd57e3e148c86db49..cda1293fc1c39be003a8c2d37efffcd8b79ab629 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -246,7 +246,7 @@ export function getValueInAnnotation(node: arkts.ClassProperty, decoratorName: D } export interface ProvideOptions { - alias: string; + alias: string | undefined; allowOverride: boolean; } @@ -255,7 +255,7 @@ export function getValueInProvideAnnotation(node: arkts.ClassProperty): ProvideO for (let i = 0; i < annotations.length; i++) { const anno: arkts.AnnotationUsage = annotations[i]; if (anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.PROVIDE) { - const alias: string = getValueInObjectAnnotation(anno, DecoratorNames.PROVIDE, 'alias'); + const alias = getValueInObjectAnnotation(anno, DecoratorNames.PROVIDE, 'alias') as string | undefined; const allowOverride: boolean = getValueInObjectAnnotation(anno, DecoratorNames.PROVIDE, 'allowOverride') ? true : false; @@ -265,7 +265,11 @@ export function getValueInProvideAnnotation(node: arkts.ClassProperty): ProvideO return undefined; } -function getValueInObjectAnnotation(anno: arkts.AnnotationUsage, decoratorName: DecoratorNames, key: string): any { +function getValueInObjectAnnotation( + anno: arkts.AnnotationUsage, + decoratorName: DecoratorNames, + key: string +): string | boolean | undefined { const isSuitableAnnotation: boolean = !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName; if (!isSuitableAnnotation) { diff --git a/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts index d6285abd1be1ad49e2bbb4fed6606bab9c809ab3..278a9bc7326969f423e16e8d080bb99fe4ab6b15 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts @@ -67,18 +67,26 @@ function reportTraceMemberVariableError(context: UISyntaxRuleContext, hasTraceDe } function tracePerportyRule( - context: UISyntaxRuleContext, - currentNode: arkts.AstNode, - hasTraceDecorator: arkts.AnnotationUsage): void { - if (arkts.isStructDeclaration(currentNode)) { - reportTraceDecoratorError(context, hasTraceDecorator); - } else if (arkts.isClassDeclaration(currentNode)) { - // The '@Trace' decorator can only be used in a 'class' decorated with '@ObservedV2' - if (!currentNode.definition?.annotations?.some((annotation: any) => annotation.expr.name === - PresetDecorators.OBSERVED_V2)) { - reportTraceInObservedV2Error(context, hasTraceDecorator, currentNode); + context: UISyntaxRuleContext, + currentNode: arkts.AstNode, + hasTraceDecorator: arkts.AnnotationUsage +): void { + if (arkts.isStructDeclaration(currentNode)) { + reportTraceDecoratorError(context, hasTraceDecorator); + } else if (arkts.isClassDeclaration(currentNode)) { + // The '@Trace' decorator can only be used in a 'class' decorated with '@ObservedV2' + if ( + !currentNode.definition?.annotations?.some((annotation: arkts.AnnotationUsage) => { + return ( + !!annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.OBSERVED_V2 + ); + }) + ) { + reportTraceInObservedV2Error(context, hasTraceDecorator, currentNode); + } } - } } function reportTraceDecoratorError(context: UISyntaxRuleContext, hasTraceDecorator: arkts.AnnotationUsage) diff --git a/bundle.json b/bundle.json index db3e1fcbf71627ecad838c0e8b780351433e5d05..3754e3cfc32cb7c4a5590e1603c66d3ed8743a6f 100644 --- a/bundle.json +++ b/bundle.json @@ -35,7 +35,8 @@ "//developtools/ace_ets2bundle:server", "//developtools/ace_ets2bundle:codegen", "//developtools/ace_ets2bundle:ets_loader_declaration", - "//developtools/ace_ets2bundle:ets_loader_ark_hap" + "//developtools/ace_ets2bundle:ets_loader_ark_hap", + "//developtools/ace_ets2bundle/koala-wrapper:ohos_ets_koala_wrapper(//build/toolchain/linux:clang_x64)" ], "inner_kits": [ { diff --git a/compiler/compile_plugin.js b/compiler/compile_plugin.js index 675dae30d032c92bd3a0bdf3ddaf5808ce654a26..73297fc827bb12308af49e2f16592309729388dc 100644 --- a/compiler/compile_plugin.js +++ b/compiler/compile_plugin.js @@ -28,7 +28,24 @@ const { generateConsumerObConfigFile } = require('./lib/fast_build/ark_compiler/ const { etsStandaloneChecker } = require('./lib/ets_checker'); const { memoryMonitor } = require('./lib/fast_build/meomry_monitor/rollup-plugin-memory-monitor'); +let initConfigForInterop = (interopConfig) => { + return {}; +}; + +try { + ({ initConfigForInterop } = require('./lib/fast_build/ark_compiler/interop/interop_manager')); +} catch (err) { + if (err.code !== 'MODULE_NOT_FOUND') { + throw err; + } +} + +function initInteropConfig(interopConfig) { + return initConfigForInterop(interopConfig); +} + exports.initConfig = initConfig; +exports.initInteropConfig = initInteropConfig; exports.getCleanConfig = getCleanConfig; exports.generateConsumerObConfigFile = generateConsumerObConfigFile; exports.etsStandaloneChecker = etsStandaloneChecker; diff --git a/compiler/main.js b/compiler/main.js index c1e72c927f6b4e5abbe1d3bd031815120cd3fc7a..b8b98b1cfd745b762524a6cb2f6dd9a7cca47969 100644 --- a/compiler/main.js +++ b/compiler/main.js @@ -72,6 +72,8 @@ let sdkConfigPrefix = 'ohos|system|kit|arkts'; let ohosSystemModulePaths = []; let ohosSystemModuleSubDirPaths = []; let allModulesPaths = []; +let externalApiCheckPlugin = new Map(); + function initProjectConfig(projectConfig) { initProjectPathConfig(projectConfig); @@ -155,6 +157,7 @@ function loadEntryObj(projectConfig) { setFaTestRunnerFile(projectConfig); } if (process.env.aceModuleJsonPath) { + setStartupPagesForObf(projectConfig); setIntentEntryPages(projectConfig); setAbilityPages(projectConfig); setStageTestRunnerFile(projectConfig); @@ -206,11 +209,42 @@ function loadEntryObj(projectConfig) { } } +function readJsonFile(filePath) { + try { + if (filePath && fs.existsSync(filePath)) { + return JSON.parse(fs.readFileSync(filePath).toString()); + } + } catch (e) { + throw Error('\x1B[31m' + `BUIDERROR: the ${filePath} file format is invalid.` + + '\x1B[39m').message; + } + return null; +} + +function setStartupPagesForObf(projectConfig) { + const moduleJson = readJsonFile(projectConfig.aceModuleJsonPath); + if (moduleJson && moduleJson.module && moduleJson.module.appStartup) { + const startupFileName = `${moduleJson.module.appStartup.replace(/\$profile\:/, '')}.json`; + const startupFilePath = path.resolve(projectConfig.aceProfilePath, startupFileName); + const startupConfig = readJsonFile(startupFilePath); + if (!startupConfig) { + return; + } + setEntryArrayForObf(startupConfig.configEntry); + startupConfig.startupTasks.forEach(task => { + if (task.srcEntry) { + setEntryArrayForObf(task.srcEntry); + } + }); + } +} + function loadNavigationConfig(aceBuildJson) { if (aceBuildJson && aceBuildJson.routerMap && Array.isArray(aceBuildJson.routerMap)) { aceBuildJson.routerMap.forEach((item) => { if (item.pageSourceFile && (item.name || item.name === '') && item.buildFunction) { const filePath = path.resolve(item.pageSourceFile); + setEntryArrayForObf(filePath); const storedFileInfo = getStoredFileInfo(); if (storedFileInfo.routerInfo.has(filePath)) { storedFileInfo.routerInfo.get(filePath).push({name: item.name, buildFunction: item.buildFunction}); @@ -760,6 +794,19 @@ function filterWorker(workerPath) { sdkConfigs = [...defaultSdkConfigs, ...extendSdkConfigs]; })(); +function collectExternalApiCheckPlugin(sdkConfig, sdkPath) { + const pluginConfigs = sdkConfig.apiCheckPlugin; + pluginConfigs.forEach(config => { + const pluginPrefix = sdkConfig.osName + '/' + config.tag; + config.path = path.resolve(sdkPath, config.path); + if (externalApiCheckPlugin.get(pluginPrefix)) { + externalApiCheckPlugin.set(pluginPrefix, externalApiCheckPlugin.get(pluginPrefix).push(...pluginConfigs)); + } else { + externalApiCheckPlugin.set(pluginPrefix, [...pluginConfigs]); + } + }); +} + function collectExternalModules(sdkPaths) { for (let i = 0; i < sdkPaths.length; i++) { const sdkPath = sdkPaths[i]; @@ -771,6 +818,10 @@ function collectExternalModules(sdkPaths) { if (!sdkConfig.apiPath) { continue; } + + if (sdkConfig.apiCheckPlugin && sdkConfig.apiCheckPlugin.length > 0) { + collectExternalApiCheckPlugin(sdkConfig, sdkPath); + } let externalApiPathArray = []; if (Array.isArray(sdkConfig.apiPath)) { externalApiPathArray = sdkConfig.apiPath; @@ -1096,6 +1147,7 @@ function resetMain() { ohosSystemModulePaths = []; ohosSystemModuleSubDirPaths = []; allModulesPaths = []; + externalApiCheckPlugin = new Map(); } function resetAbilityConfig() { @@ -1186,3 +1238,5 @@ exports.resetGlobalProgram = resetGlobalProgram; exports.setEntryArrayForObf = setEntryArrayForObf; exports.getPackageJsonEntryPath = getPackageJsonEntryPath; exports.setIntentEntryPages = setIntentEntryPages; +exports.externalApiCheckPlugin = externalApiCheckPlugin; +exports.setStartupPagesForObf = setStartupPagesForObf; diff --git a/compiler/src/ets_checker.ts b/compiler/src/ets_checker.ts index 716d75244c5fed4ebfac63401cfc2f77aa6daec1..97422740d670db7013dea0bea1427fe7eee5fb85 100644 --- a/compiler/src/ets_checker.ts +++ b/compiler/src/ets_checker.ts @@ -110,9 +110,11 @@ export interface LanguageServiceCache { service?: ts.LanguageService; pkgJsonFileHash?: string; targetESVersion?: ts.ScriptTarget; + types?: string[]; maxFlowDepth?: number; preTsImportSendable?: boolean; preSkipOhModulesLint?: boolean; + preEnableStrictCheckOHModule?: boolean; preMixCompile?: boolean; } @@ -177,7 +179,8 @@ function setCompilerOptions(resolveModulePaths: string[]): void { allPath.push('../*'); } } - const suffix: string = projectConfig.hotReload ? HOT_RELOAD_BUILD_INFO_SUFFIX : TS_BUILD_INFO_SUFFIX; + const suffix: string = projectConfig?.hotReload ? HOT_RELOAD_BUILD_INFO_SUFFIX : + `${TS_BUILD_INFO_SUFFIX}${projectConfig?.widgetCompile === 'true' ? '_widget' : ''}`; const buildInfoPath: string = path.resolve(projectConfig.cachePath, '..', suffix); checkArkTSVersion(); Object.assign(compilerOptions, { @@ -210,7 +213,10 @@ function setCompilerOptions(resolveModulePaths: string[]): void { 'compatibleSdkVersionStage': projectConfig.compatibleSdkVersionStage, 'compatibleSdkVersion': projectConfig.compatibleSdkVersion, 'skipOhModulesLint': skipOhModulesLint, - 'mixCompile': mixCompile + 'enableStrictCheckOHModule': enableStrictCheckOHModule, + 'mixCompile': mixCompile, + 'isCompileJsHar': isCompileJsHar(), + 'moduleRootPath': projectConfig.moduleRootPath, }); if (projectConfig.compileMode === ESMODULE) { Object.assign(compilerOptions, { @@ -224,6 +230,11 @@ function setCompilerOptions(resolveModulePaths: string[]): void { readTsBuildInfoFileInCrementalMode(buildInfoPath, projectConfig); } +function isCompileJsHar(): boolean { + return projectConfig.compileHar && projectConfig.byteCodeHar === false && projectConfig.buildMode === 'Release' && + projectConfig.obfuscationOptions?.selfConfig.ruleOptions.enable && !projectConfig.useTsHar; +} + function checkArkTSVersion(): void { const etsCheckerLogger = fastBuildLogger || logger; if (getArkTSVersion() === ArkTSVersion.ArkTS_1_0 && tsImportSendable) { @@ -437,30 +448,31 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi let service: ts.LanguageService | undefined = cache?.service; const currentHash: string | undefined = rollupShareObject?.projectConfig?.pkgJsonFileHash; const currentTargetESVersion: ts.ScriptTarget = compilerOptions.target; + const currentTypes: string[] | undefined = compilerOptions.types; const currentMaxFlowDepth: number | undefined = compilerOptions.maxFlowDepth; const lastHash: string | undefined = cache?.pkgJsonFileHash; const lastTargetESVersion: ts.ScriptTarget | undefined = cache?.targetESVersion; + const lastTypes: string[] | undefined = cache?.types; const lastMaxFlowDepth: number | undefined = cache?.maxFlowDepth; const hashDiffers: boolean | undefined = currentHash && lastHash && currentHash !== lastHash; const shouldRebuildForDepDiffers: boolean | undefined = reuseLanguageServiceForDepChange ? (hashDiffers && !rollupShareObject?.depInfo?.enableIncre) : hashDiffers; const targetESVersionDiffers: boolean | undefined = lastTargetESVersion && currentTargetESVersion && lastTargetESVersion !== currentTargetESVersion; + const typesDiff: boolean | undefined = lastTypes && currentTypes && !areEqualArrays(lastTypes, currentTypes); const maxFlowDepthDiffers: boolean | undefined = lastMaxFlowDepth && currentMaxFlowDepth && lastMaxFlowDepth !== currentMaxFlowDepth; - const tsImportSendableDiff: boolean = (cache?.preTsImportSendable === undefined && !tsImportSendable) ? - false : - cache?.preTsImportSendable !== tsImportSendable; - const skipOhModulesLintDiff: boolean = (cache?.preSkipOhModulesLint === undefined && !skipOhModulesLint) ? - false : cache?.preSkipOhModulesLint !== skipOhModulesLint; - const mixCompileDiff: boolean = (cache?.preMixCompile === undefined && !mixCompile) ? - false : cache?.preMixCompile !== mixCompile; - const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || - tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff; + const tsImportSendableDiff: boolean = checkValueDiff(cache?.preTsImportSendable, tsImportSendable); + const skipOhModulesLintDiff: boolean = checkValueDiff(cache?.preSkipOhModulesLint, skipOhModulesLint); + const enableStrictCheckOHModuleDiff: boolean = checkValueDiff(cache?.preEnableStrictCheckOHModule, enableStrictCheckOHModule); + const mixCompileDiff: boolean = checkValueDiff(cache?.preMixCompile, mixCompile); + const onlyDeleteBuildInfoCache: boolean | undefined = tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || + enableStrictCheckOHModuleDiff || mixCompileDiff || typesDiff; + const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || onlyDeleteBuildInfoCache; if (reuseLanguageServiceForDepChange && hashDiffers && rollupShareObject?.depInfo?.enableIncre) { needReCheckForChangedDepUsers = true; } if (!service || shouldRebuild) { - rebuildProgram(targetESVersionDiffers, tsImportSendableDiff, maxFlowDepthDiffers, skipOhModulesLintDiff, mixCompileDiff); + rebuildProgram(targetESVersionDiffers, onlyDeleteBuildInfoCache); service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); } else { // Found language service from cache, update root files @@ -472,6 +484,7 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi service: service, pkgJsonFileHash: currentHash, targetESVersion: currentTargetESVersion, + types: currentTypes, maxFlowDepth: currentMaxFlowDepth, preTsImportSendable: tsImportSendable, preSkipOhModulesLint: skipOhModulesLint, @@ -481,14 +494,43 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi return service; } -function rebuildProgram(targetESVersionDiffers: boolean | undefined, tsImportSendableDiff: boolean, - maxFlowDepthDiffers: boolean | undefined, skipOhModulesLintDiff: boolean, mixCompileDiff: boolean): void { +/** + * compare cache value and current value, check if they are different + * @param cacheValue cache value + * @param currentValue current value + * @returns true if they are different, false otherwise + */ +function checkValueDiff(cacheValue: boolean | undefined, currentValue: boolean): boolean { + return !(cacheValue === undefined && !currentValue) && cacheValue !== currentValue; +} + +export function areEqualArrays(lastArray: string[] | undefined, currentArray: string[] | undefined): boolean { + if (!lastArray || !currentArray) { + return lastArray === currentArray; + } + + const currentSet: Set = new Set(currentArray!); + const lastSet: Set = new Set(lastArray!); + + if (lastSet.size !== currentSet.size) { + return false; + } + + for (const item of lastSet) { + if (!currentSet.has(item)) { + return false; + } + } + return true; +} + +function rebuildProgram(targetESVersionDiffers: boolean | undefined, onlyDeleteBuildInfoCache: boolean | undefined): void { if (targetESVersionDiffers) { // If the targetESVersion is changed, we need to delete the build info cahce files deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); targetESVersionChanged = true; - } else if (tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff) { - // When tsImportSendable or maxFlowDepth is changed, we need to delete the build info cahce files + } else if (onlyDeleteBuildInfoCache) { + // When tsImportSendable or types or maxFlowDepth or skipOhModuleslint or mixCompile is changed, we need to delete the build info cahce files deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); } } @@ -548,6 +590,7 @@ export const warnCheckerResult: WarnCheckerResult = { count: 0 }; export let languageService: ts.LanguageService = null; let tsImportSendable: boolean = false; let skipOhModulesLint: boolean = false; +let enableStrictCheckOHModule: boolean = false; let mixCompile: boolean = false; export let maxMemoryInServiceChecker: number = 0; export function serviceChecker(rootFileNames: string[], newLogger: Object = null, resolveModulePaths: string[] = null, @@ -556,6 +599,7 @@ export function serviceChecker(rootFileNames: string[], newLogger: Object = null let cacheFile: string = null; tsImportSendable = rollupShareObject?.projectConfig.tsImportSendable; skipOhModulesLint = rollupShareObject?.projectConfig.skipOhModulesLint; + enableStrictCheckOHModule = rollupShareObject?.projectConfig.enableStrictCheckOHModule; mixCompile = rollupShareObject?.projectConfig.mixCompile; if (projectConfig.xtsMode || process.env.watchMode === 'true') { if (projectConfig.hotReload) { @@ -568,7 +612,8 @@ export function serviceChecker(rootFileNames: string[], newLogger: Object = null MemoryMonitor.stopRecordStage(recordInfo); props = languageService.getProps(); } else { - cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache'); + cacheFile = rollupShareObject?.projectConfig?.widgetCompile === 'true' ? path.resolve(projectConfig.cachePath, '../.ts_checker_cache_widget') : + path.resolve(projectConfig.cachePath, '../.ts_checker_cache'); const [isJsonObject, cacheJsonObject]: [boolean, WholeCache | undefined] = isJsonString(cacheFile); const wholeCache: WholeCache = isJsonObject ? cacheJsonObject : { 'runtimeOS': projectConfig.runtimeOS, 'sdkInfo': projectConfig.sdkInfo, 'fileList': {} }; if (wholeCache.runtimeOS === projectConfig.runtimeOS && wholeCache.sdkInfo === projectConfig.sdkInfo) { @@ -766,7 +811,7 @@ function processBuildHap(cacheFile: string, rootFileNames: string[], parentEvent if ((/\.d\.e?ts$/).test(moduleFile)) { generateSourceFilesInHar(moduleFile, fs.readFileSync(moduleFile, 'utf-8'), path.extname(moduleFile), projectConfig, projectConfig.modulePathMap); - } else if ((/\.e?ts$/).test(moduleFile)) { + } else if ((/\.e?ts$/).test(moduleFile) && !toUnixPath(moduleFile).includes('/oh_modules/')) { emit = undefined; let sourcefile = globalProgram.program.getSourceFile(moduleFile); if (sourcefile) { @@ -784,8 +829,16 @@ function processBuildHap(cacheFile: string, rootFileNames: string[], parentEvent } function printDeclarationDiagnostics(errorCodeLogger?: Object | undefined): void { - globalProgram.builderProgram.getDeclarationDiagnostics().forEach((diagnostic: ts.Diagnostic) => { - printDiagnostic(diagnostic, ErrorCodeModule.TSC, errorCodeLogger); + globalProgram.program.getSourceFiles().forEach((sourceFile: ts.SourceFile) => { + if ((/\.d\.e?ts$/).test(sourceFile.fileName) || (/\.js$/).test(sourceFile.fileName)) { + return; + } + if (toUnixPath(sourceFile.fileName).includes('/oh_modules/')) { + return; + } + globalProgram.builderProgram.getDeclarationDiagnostics(sourceFile).forEach((diagnostic: ts.Diagnostic) => { + printDiagnostic(diagnostic, ErrorCodeModule.TSC, errorCodeLogger); + }); }); } @@ -1877,8 +1930,6 @@ export function etsStandaloneChecker(entryObj, logger, projectConfig): void { export function resetEtsCheckTypeScript(): void { if (globalProgram.program) { globalProgram.program.releaseTypeChecker(); - } else if (languageService) { - languageService.getProgram().releaseTypeChecker(); } resetGlobalProgram(); languageService = null; diff --git a/compiler/src/fast_build/ark_compiler/generate_sourcemap.ts b/compiler/src/fast_build/ark_compiler/generate_sourcemap.ts index 732ef220f1f1a6501a1dbc3086acf4f5d5a9ddfe..292b37b469ab2387067ecbabeaf3e5891e7ba07d 100644 --- a/compiler/src/fast_build/ark_compiler/generate_sourcemap.ts +++ b/compiler/src/fast_build/ark_compiler/generate_sourcemap.ts @@ -81,8 +81,8 @@ export class SourceMapGenerator { private logger: CommonLogger; private isFirstAppend: boolean = true; private isCompileSingle: boolean = false; - private originFd: number; - private tempFd: number; + private originFileEdited: boolean = false; + private tempFileEdited: boolean = false; public sourceMapKeyMappingForObf: Map = new Map(); @@ -227,7 +227,7 @@ export class SourceMapGenerator { } public writeOrigin(content: string): void { - if (!this.originFd) { + if (!this.originFileEdited) { if (fs.existsSync(this.sourceMapPath)) { fs.unlinkSync(this.sourceMapPath); } @@ -235,13 +235,13 @@ export class SourceMapGenerator { if (!fs.existsSync(sourceMapPathDir)) { fs.mkdirSync(sourceMapPathDir, { recursive: true }); } - this.originFd = fs.openSync(this.sourceMapPath, 'a'); + this.originFileEdited = true; } - fs.appendFileSync(this.originFd, content, { encoding: 'utf8' }); + fs.appendFileSync(this.sourceMapPath, content, 'utf8'); } public writeTemp(content: string): void { - if (!this.tempFd) { + if (!this.tempFileEdited) { if (fs.existsSync(this.sourceMapPathTmp)) { fs.unlinkSync(this.sourceMapPathTmp); } @@ -249,20 +249,14 @@ export class SourceMapGenerator { if (!fs.existsSync(sourceMapPathTmpDir)) { fs.mkdirSync(sourceMapPathTmpDir, { recursive: true }); } - this.tempFd = fs.openSync(this.sourceMapPathTmp, 'a'); + this.tempFileEdited = true; } - fs.appendFileSync(this.tempFd, content, { encoding: 'utf8' }); + fs.appendFileSync(this.sourceMapPathTmp, content, 'utf8'); } - public closeFd(): void { - if (this.originFd) { - fs.closeSync(this.originFd); - this.originFd = undefined; - } - if (this.tempFd) { - fs.closeSync(this.tempFd); - this.tempFd = undefined; - } + public resetFileEdited(): void { + this.originFileEdited = false; + this.tempFileEdited = false; } public convertSourceMapToCache(maps: Object): string { @@ -318,7 +312,7 @@ export class SourceMapGenerator { if (!fs.existsSync(this.sourceMapPathTmp)) { this.writeTemp(''); } - this.closeFd(); + this.resetFileEdited(); if (fs.existsSync(this.cacheSourceMapPath)) { fs.unlinkSync(this.cacheSourceMapPath); } @@ -625,7 +619,7 @@ export class SourceMapGenerator { public static cleanSourceMapObject(): void { if (this.instance) { - this.instance.closeFd(); + this.instance.resetFileEdited(); this.instance.keyCache.clear(); this.instance.sourceMaps = undefined; this.instance = undefined; diff --git a/compiler/src/fast_build/common/rollup-plugin-watch-change.ts b/compiler/src/fast_build/common/rollup-plugin-watch-change.ts index 69803a7520a185c7c6bee39ad9eb4ce4925f0271..13197d6774f45b43c1a32ab7d06a00cb81ad72e9 100644 --- a/compiler/src/fast_build/common/rollup-plugin-watch-change.ts +++ b/compiler/src/fast_build/common/rollup-plugin-watch-change.ts @@ -60,8 +60,9 @@ export function watchChangeFiles() { resources.app = {}; readAppResource(process.env.appResource); if (process.env.rawFileResource) { - resourcesRawfile(process.env.rawFileResource, storedFileInfo.resourcesArr); - this.share.rawfilechanged = differenceResourcesRawfile(storedFileInfo.lastResourcesSet, storedFileInfo.resourcesArr); + resourcesRawfile(process.env.rawFileResource, storedFileInfo.resourcesArr, this.share.getHashByFilePath); + this.share.rawfilechanged = differenceResourcesRawfile(storedFileInfo.lastResourcesSet, + storedFileInfo.resourcesArr, storedFileInfo.changedResourcesSet); } } ShouldEnableDebugLine.enableDebugLine = false; diff --git a/compiler/src/fast_build/ets_ui/rollup-plugin-ets-typescript.ts b/compiler/src/fast_build/ets_ui/rollup-plugin-ets-typescript.ts index 8f0e19096c58b10c7b25eb675f08ef6a2a1f9f3f..3a4e9f0cd1c26b9dddc0c4ea3364a3a95923e342 100644 --- a/compiler/src/fast_build/ets_ui/rollup-plugin-ets-typescript.ts +++ b/compiler/src/fast_build/ets_ui/rollup-plugin-ets-typescript.ts @@ -161,12 +161,19 @@ export function etsTransform() { cacheFile = this.cache.get('transformCacheFiles'); storedFileInfo.addGlobalCacheInfo(this.cache.get('resourceListCacheInfo'), this.cache.get('resourceToFileCacheInfo'), cacheFile); - if (this.cache.get('lastResourcesArr')) { - storedFileInfo.lastResourcesSet = new Set([...this.cache.get('lastResourcesArr')]); + if (this.cache.has('lastResourcesArr') && this.cache.has('lastResourcesForFiles')) { + storedFileInfo.lastResourcesSet = this.cache.get('lastResourcesArr'); + storedFileInfo.lastResourcesForFiles = this.cache.get('lastResourcesForFiles'); + storedFileInfo.hasResourcesCache = true; + } else { + storedFileInfo.lastResourcesSet = new Map(); + storedFileInfo.lastResourcesForFiles = new Map(); + storedFileInfo.hasResourcesCache = false; } if (process.env.rawFileResource) { - resourcesRawfile(process.env.rawFileResource, storedFileInfo.resourcesArr); - this.share.rawfilechanged = differenceResourcesRawfile(storedFileInfo.lastResourcesSet, storedFileInfo.resourcesArr); + resourcesRawfile(process.env.rawFileResource, storedFileInfo.resourcesArr, this.share.getHashByFilePath); + this.share.rawfilechanged = differenceResourcesRawfile(storedFileInfo.lastResourcesSet, + storedFileInfo.resourcesArr, storedFileInfo.changedResourcesSet); } } if (!!this.cache.get('enableDebugLine') !== projectConfig.enableDebugLine) { @@ -198,7 +205,7 @@ export function etsTransform() { const fileName: string = path.resolve(options.id); let shouldDisable: boolean = shouldDisableCache || disableNonEntryFileCache(fileName) || ShouldEnableDebugLine.enableDebugLine; if (process.env.compileMode === 'moduleJson') { - shouldDisable = shouldDisable || storedFileInfo.shouldInvalidFiles.has(fileName) || this.share.rawfilechanged; + shouldDisable = shouldDisable || storedFileInfo.shouldInvalidFiles.has(fileName) || checkRawFileChange(fileName); if (cacheFile && cacheFile[fileName] && cacheFile[fileName].children.length) { for (let child of cacheFile[fileName].children) { const newTimeMs: number = fs.existsSync(child.fileName) ? fs.statSync(child.fileName).mtimeMs : -1; @@ -280,7 +287,11 @@ export function etsTransform() { } shouldDisableCache = false; this.cache.set('disableCacheOptions', disableCacheOptions); - this.cache.set('lastResourcesArr', [...storedFileInfo.resourcesArr]); + this.cache.set('lastResourcesArr', new Map(storedFileInfo.resourcesArr)); + storedFileInfo.resourcesForFiles.forEach((value, key) => { + storedFileInfo.lastResourcesForFiles.set(key, value); + }); + this.cache.set('lastResourcesForFiles', new Map(storedFileInfo.lastResourcesForFiles)); if (projectConfig.enableDebugLine) { this.cache.set('enableDebugLine', true); } else { @@ -338,6 +349,33 @@ function judgeCacheShouldDisabled(): void { } } + +/** + * Checks if a given file needs to be recompiled due to changes in raw resource files. + * + * This function determines whether the specified file has dependencies on raw files + * (resources) that have been modified since the last compilation. It checks against + * a cache of stored file information and a set of changed resources. + * + * @param {string} file - The path or identifier of the file to check for recompilation need + * @returns {boolean} Returns true if the file needs recompilation due to raw file changes, + * false otherwise + */ +function checkRawFileChange(file: string): boolean { + if (!storedFileInfo.hasResourcesCache) { + return true; + } + if (!storedFileInfo.lastResourcesForFiles.has(file)) { + return false; + } + for (const singleRawfiles of storedFileInfo.lastResourcesForFiles.get(file)) { + if (storedFileInfo.changedResourcesSet.has(singleRawfiles)) { + return true; + } + } + return false; +} + interface EmitResult { outputText: string, sourceMapText: string, @@ -417,7 +455,7 @@ async function transform(code: string, id: string) { const result: ts.TranspileOutput = ts.transpileModule(newContent, { compilerOptions: compilerOptions, fileName: id, - transformers: { before: [processUISyntax(null, false, null, id, metaInfo)] } + transformers: { before: [processUISyntax(null, false, undefined, id, null, metaInfo)] } }); resetCollection(); diff --git a/compiler/src/fast_build/system_api/api_check_utils.ts b/compiler/src/fast_build/system_api/api_check_utils.ts index 71be695cd3d84c08684a8ec0b880aead0e20a94a..8eff0797fc03166a3942394b08478986100d0cc7 100644 --- a/compiler/src/fast_build/system_api/api_check_utils.ts +++ b/compiler/src/fast_build/system_api/api_check_utils.ts @@ -24,13 +24,14 @@ import { ohosSystemModulePaths, systemModules, allModulesPaths, - ohosSystemModuleSubDirPaths + ohosSystemModuleSubDirPaths, + externalApiCheckPlugin } from '../../../main'; import { LogType, LogInfo, IFileLog, - CurrentProcessFile + CurrentProcessFile, } from '../../utils'; import { type ResolveModuleInfo } from '../../ets_checker'; import { @@ -73,6 +74,7 @@ import { VERSION_CHECK_FUNCTION_NAME } from './api_check_define'; import { JsDocCheckService } from './api_check_permission'; +import { SdkVersionValidator } from './sdk_version_validator'; /** * bundle info @@ -89,7 +91,7 @@ export interface CheckValidCallbackInterface { } export interface CheckJsDocSpecialValidCallbackInterface { - (jsDocTags: readonly ts.JSDocTag[], config: ts.JsDocNodeCheckConfigItem): boolean; + (jsDocTags: readonly ts.JSDocTag[], config: ts.JsDocNodeCheckConfigItem, node?: ts.Node): boolean; } export interface checkConditionValidCallbackInterface { @@ -545,32 +547,152 @@ function collectExternalSyscapInfos( } /** - * Determine the necessity of since check. + * Validates if a since tag requires version checking based on JSDoc and project configuration. * - * @param jsDocTags - * @param config - * @returns + * @param jsDocTags - Array of JSDoc tags to analyze + * @param config - Configuration object that will receive error messages + * @param node - Optional TypeScript node for additional validation + * @returns True if since check is required and validation fails, false otherwise */ -function checkSinceValue(jsDocTags: readonly ts.JSDocTag[], config: ts.JsDocNodeCheckConfigItem): boolean { +function checkSinceValue( + jsDocTags: readonly ts.JSDocTag[], + config: ts.JsDocNodeCheckConfigItem, + node?: ts.Node +): boolean { + // Validate basic input structure if (!jsDocTags[0]?.parent?.parent || !projectConfig.compatibleSdkVersion) { return false; } - const currentNode: HasJSDocNode = jsDocTags[0].parent.parent as HasJSDocNode; - if (!currentNode.jsDoc) { + + const jsDocNode = jsDocTags[0].parent.parent as HasJSDocNode; + if (!jsDocNode?.jsDoc) { + return false; + } + const apiMinVersion = getMinVersion(jsDocNode.jsDoc); + if (!apiMinVersion) { return false; } - const minSince: string = getMinVersion(currentNode.jsDoc); - const hasSince: boolean = minSince !== ''; - const compatibleSdkVersion: string = projectConfig.compatibleSdkVersion.toString(); - if (!isCompliantSince(minSince) || !isCompliantSince(compatibleSdkVersion)) { + const sourceFile = jsDocNode.getSourceFile(); + if (!sourceFile) { return false; } - if (hasSince && comparePointVersion(compatibleSdkVersion.toString(), minSince) === -1) { - config.message = SINCE_TAG_CHECK_ERROER.replace('$SINCE1', minSince).replace('$SINCE2', compatibleSdkVersion); - return true; + + // Perform version validation check + const versionChecker = getVersionValidationFunction(); + let projectTargetVersion: string; + if (versionChecker === compareVersionsWithPointSystem) { + projectTargetVersion = projectConfig.compatibleSdkVersion.toString(); + } else { + projectTargetVersion = projectConfig.originCompatibleSdkVersion?.toString() || projectConfig.compatibleSdkVersion.toString(); } - return false; + const validationResult = versionChecker(apiMinVersion, projectTargetVersion, 0); + if (validationResult.result) { + return false; + } + + // Check if SDK version is already properly handled in code + const typeChecker = CurrentProcessFile.getChecker(); + const sdkValidator = new SdkVersionValidator(projectTargetVersion, apiMinVersion, typeChecker); + + if (sdkValidator.isSdkApiVersionHandled(node)) { + return false; + } + + config.message = SINCE_TAG_CHECK_ERROER + .replace('$SINCE1', apiMinVersion) + .replace('$SINCE2', projectTargetVersion); + + return true; +} + +/** + * Retrieves the appropriate version validation function. + * First attempts to load external plugin validation, falls back to default if unavailable. + * + * @returns Version validation function (external or default) + */ +export function getVersionValidationFunction(): VersionValidationFunction { + if (projectConfig.originCompatibleSdkVersion?.toString() === undefined) { + return compareVersionsWithPointSystem; + } + const pluginKey = getPluginKey(projectConfig.runtimeOS, SINCE_TAG_NAME); + const plugins = externalApiCheckPlugin.get(pluginKey); + + // Check if external plugins exist and try to load them + if (plugins && plugins.length > 0) { + try { + for (const plugin of plugins) { + const externalMethod = require(plugin.path)[plugin.functionName]; + if (typeof externalMethod === 'function') { + return externalMethod; + } + } + } catch (error) { + console.warn(`Failed to load external version validator: ${error}`); + } + } + + return compareVersionsWithPointSystem; +} + +/** + * Default version comparison function using point-based version numbering. + * Validates version format and compares numerical values. + * + * @param sinceVersion - Minimum version requirement + * @param targetVersion - Current project target version + * @param _triggerScene - Trigger scene identifier (unused in default implementation) + * @returns Validation result with success status and optional message + */ +export function compareVersionsWithPointSystem( + sinceVersion: string, + targetVersion: string, + _triggerScene: number +): VersionValidationResult { + // Validate version format + if (!isCompliantSince(sinceVersion) || !isCompliantSince(targetVersion)) { + return { + result: true, + message: 'Invalid version number format' + }; + } + const triggerResult = comparePointVersion(targetVersion, sinceVersion); + const isTargetGreaterOrEqual = triggerResult >= 0; + + return { + result: isTargetGreaterOrEqual, + message: isTargetGreaterOrEqual ? 'Version requirement satisfied' : 'API version requirement not met' + }; +} + +/** + * Function signature for version validation. + * Takes two version strings and a trigger scene (0-1-2), returns validation result. + */ +export interface VersionValidationFunction { + (sinceVersion: string, targetVersion: string, triggerScene: number): VersionValidationResult; +} + +/** + * Result object returned by version validation functions. + */ +export interface VersionValidationResult { + result: boolean; + message?: string; +} + +/** + * 获取externalApiCheckPlugin的key + * @param { string } apiFilePath + * @param { string } tagName + * @returns { string } + */ +function getPluginKey(apiFilePath: string, tagName: string): string { + const apiFileName: string = path.basename(apiFilePath); + const apiPrefix: string = apiFileName.split('.')[0]; + const pluginKey: string = apiPrefix + '/' + tagName; + return pluginKey; } /** @@ -809,19 +931,18 @@ function getMinVersion(jsDocs: ts.JSDoc[]): string { * @param { string } secondVersion * @returns { number } */ -function comparePointVersion(firstVersion: string, secondVersion: string): number { - const firstPointVersion = firstVersion.split('.'); - const secondPointVersion = secondVersion.split('.'); +function comparePointVersion(firstVersion: string, secondVersion: string): -1 | 0 | 1 { + const firstParts = firstVersion.split('.'); + const secondParts = secondVersion.split('.'); + for (let i = 0; i < 3; i++) { - const part1 = parseInt(firstPointVersion[i] || '0', 10); - const part2 = parseInt(secondPointVersion[i] || '0', 10); + const part1 = parseInt(firstParts[i] || '0', 10); + const part2 = parseInt(secondParts[i] || '0', 10); - if (part1 < part2) { - return -1; - } - if (part1 > part2) { - return 1; + if (part1 !== part2) { + return part1 > part2 ? 1 : -1; } } + return 0; } \ No newline at end of file diff --git a/compiler/src/fast_build/system_api/sdk_version_validator.ts b/compiler/src/fast_build/system_api/sdk_version_validator.ts new file mode 100644 index 0000000000000000000000000000000000000000..74b9901c864024902a1848408c52d0491960c67c --- /dev/null +++ b/compiler/src/fast_build/system_api/sdk_version_validator.ts @@ -0,0 +1,556 @@ +/* + * 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 ts from 'typescript'; +import { + projectConfig +} from '../../../main'; +import { + getVersionValidationFunction +} from './api_check_utils'; + +/** + * The node is considered **valid** if it satisfies **at least one** of the following: + * 1. It is wrapped in a `try/catch` block. + * 2. It is wrapped in an `undefined` check. + * 3. It is wrapped in an SDK version comparison. + */ +export class SdkVersionValidator { + private readonly compatibleSdkVersion: string; + private readonly minSinceVersion: string; + private readonly otherSourceDeviceInfo: string = 'distributionOSApiVersion'; + private readonly openSourceDeviceInfo: string = 'sdkApiVersion'; + private readonly openSourceRuntime: string = 'OpenHarmony'; + private readonly deviceInfoChecker: Map = new Map([ + [this.otherSourceDeviceInfo, ['@ohos.deviceInfo.d.ts']], + [this.openSourceDeviceInfo, ['@ohos.deviceInfo.d.ts']] +]); + private readonly typeChecker?: ts.TypeChecker; + + + constructor(projectCompatibleSdkVersion: string, minSinceValue: string, typeChecker?: ts.TypeChecker) { + this.compatibleSdkVersion = projectCompatibleSdkVersion; + this.minSinceVersion = minSinceValue; + this.typeChecker = typeChecker; + } + + /** + * Checks whether a given node is valid for at least one condition. + * @param node - The AST node to check. + * @returns `true` if the node meets any of the handling rules, otherwise `false`. + */ + public isSdkApiVersionHandled(node: ts.Node): boolean { + if (!node) { + return false; + } + + return ( + this.isNodeWrappedInTryCatch(node) || + this.isNodeWrappedInUndefinedCheck(node) || + this.isNodeWrappedInSdkComparison(node) + ); + } + + private isNodeWrappedInTryCatch(node: ts.Node): boolean { + return this.findParentNode(node, (parent) => { + if (ts.isTryStatement(parent)) { + return node.getStart() >= parent.tryBlock.getStart(); + } + return false; + }) !== null; + } + + private isNodeWrappedInUndefinedCheck(node: ts.Node): boolean { + const targetName = this.getPrimaryNameFromNode(node); + if (!targetName) { + return false; + } + + return this.findParentNode(node, (parent) => { + if (ts.isIfStatement(parent)) { + return this.isUndefinedCheckHelper(parent.expression, targetName); + } + return false; + }) !== null; + } + + private isUndefinedCheckHelper(expression: ts.Expression, name: string): boolean { + if (!ts.isBinaryExpression(expression)) { + return false; + } + + // Check if the operator is a "not equal" comparison (!== or !=) + const isNotEqualOperator = [ + ts.SyntaxKind.ExclamationEqualsEqualsToken, + ts.SyntaxKind.ExclamationEqualsToken + ].includes(expression.operatorToken.kind); + + if (!isNotEqualOperator) { + return false; + } + + const { left, right } = expression; + + // Determine if either side is the literal "undefined" + const isLeftUndefined = this.isUndefinedNode(left); + const isRightUndefined = this.isUndefinedNode(right); + + const isLeftTarget = this.isTargetNode(left, name); + const isRightTarget = this.isTargetNode(right, name); + return (isLeftTarget && isRightUndefined) || (isLeftUndefined && isRightTarget); + } + + private isUndefinedNode(node: ts.Node): boolean { + return ts.isIdentifier(node) && node.text === 'undefined'; + } + + private isNodeWrappedInSdkComparison(node: ts.Node): boolean { + if (this.compatibleSdkVersion === '' || !this.typeChecker) { + return false; + } + + return ( + this.findParentNode(node, (parent) => this.isParentIfSdkComparison(node, parent)) !== null + ); + } + + private isParentIfSdkComparison(node: ts.Node, parent: ts.Node): boolean { + if (!ts.isIfStatement(parent)) { + return false; + } + + try { + const isInThenBlock = this.isNodeInIfThenBlock(node, parent); + if (!isInThenBlock) { + return false; + } + return this.isSdkComparisonHelper(parent.expression); + } catch { + return false; + } + } + + private isSdkComparisonHelper(expression: ts.Expression): boolean { + const expressionText = expression.getText(); + + const runtimeType = projectConfig.runtimeOS; + const matchedEntry = Array.from(this.deviceInfoChecker.entries()) + .find(([api]) => expressionText.includes(api)); + if (!matchedEntry) { + return false; + } + + const [matchedApi, validPackagePath] = matchedEntry; + if (runtimeType === this.openSourceRuntime && matchedApi === this.otherSourceDeviceInfo) { + return false; + } + + const parts = this.extractComparisonParts(expression, matchedApi); + if (!parts) { + return false; + } + + if (!this.validateSdkVersionCompatibility(parts.operator, parts.value, matchedApi, runtimeType, parts.apiPosition)) { + return false; + } + + // Try to resolve the actual identifier used for this API in the expression + const apiIdentifier = this.findValidImportApiIdentifier(expression, matchedApi); + + // Validate that the identifier comes from the allowed SDK package path + return apiIdentifier + ? this.isValidSdkDeclaration(apiIdentifier, validPackagePath) + : false; + } + + /** + * Extracts comparison parts from a binary expression and resolves declaration values. + * Also determines which side of the comparison contains the API. + * + * @param expression - The binary expression to analyze + * @param matchedApi - The API identifier to match against + * @returns Object with operator, resolved value, and API position, or undefined if invalid + */ + private extractComparisonParts( + expression: ts.Expression, + matchedApi: string + ): { operator: string; value: string; apiPosition: 'left' | 'right' } | undefined { + if (!ts.isBinaryExpression(expression)) { + return undefined; + } + + const operator = expression.operatorToken.getText(); + const left = expression.left.getText(); + const right = expression.right.getText(); + + let targetValue: string; + let valueExpression: ts.Expression; + let apiPosition: 'left' | 'right'; + + // Determine which side contains the API and get the comparison value + if (left.includes(matchedApi)) { + targetValue = right; + valueExpression = expression.right; + apiPosition = 'left'; + } else if (right.includes(matchedApi)) { + targetValue = left; + valueExpression = expression.left; + apiPosition = 'right'; + } else { + return undefined; + } + + // Try to resolve declaration value if it exists + const resolvedValue = this.resolveDeclarationValue(valueExpression, targetValue); + + return { operator, value: resolvedValue, apiPosition }; + } + + /** + * Resolves the declaration value of an expression if it exists. + * If the expression is a variable/constant reference, returns its declared value. + * Otherwise, returns the original text value. + * + * @param expression - The expression to resolve + * @param fallbackValue - The fallback text value if resolution fails + * @returns The resolved declaration value or fallback value + */ + private resolveDeclarationValue(expression: ts.Expression, fallbackValue: string): string { + if (!this.typeChecker) { + return fallbackValue; + } + + try { + // Only resolve if the expression is an identifier (variable/constant reference) + if (!ts.isIdentifier(expression)) { + return fallbackValue; + } + + const symbol = this.typeChecker.getSymbolAtLocation(expression); + if (!symbol?.declarations?.length) { + return fallbackValue; + } + + const declaration = symbol.declarations[0]; + + // Handle variable declarations with initializers + if (ts.isVariableDeclaration(declaration) && declaration.initializer) { + return this.extractValueFromInitializer(declaration.initializer); + } + + // Handle const assertions and other declaration types + if (ts.isBindingElement(declaration) && declaration.initializer) { + return this.extractValueFromInitializer(declaration.initializer); + } + + return fallbackValue; + } catch (error) { + // If resolution fails, return the original value + return fallbackValue; + } + } + + /** + * Extracts the actual value from a variable initializer expression. + * Handles literals, numeric values, and string values. + * + * @param initializer - The initializer expression + * @returns The extracted value as string + */ + private extractValueFromInitializer(initializer: ts.Expression): string { + // Handle numeric literals + if (ts.isNumericLiteral(initializer)) { + return initializer.text; + } + + // Handle string literals (remove quotes) + if (ts.isStringLiteral(initializer)) { + return initializer.text; + } + + // Handle boolean literals + if (initializer.kind === ts.SyntaxKind.TrueKeyword) { + return 'true'; + } + if (initializer.kind === ts.SyntaxKind.FalseKeyword) { + return 'false'; + } + + // Handle other expression types by returning their text + return initializer.getText(); + } + + /** + * Validates SDK version compatibility by comparing operator, value, and API type. + * + * @param operator - Comparison operator from the expression + * @param value - Version value to compare against + * @param matchedApi - The matched API identifier + * @param runtimeType - Runtime environment type (OpenHarmony or Other) + * @param apiPosition - Position of API in comparison: 'left' or 'right' + * @returns True if SDK version is compatible, false otherwise + */ + private validateSdkVersionCompatibility( + operator: string, + value: string, + matchedApi: string, + runtimeType: string, + apiPosition: 'left' | 'right' + ): boolean { + const comparisonValue = Number(value); + // comparisonValue should conform to the integer format. + if (!Number.isInteger(comparisonValue)) { + return false; + } + // Adjust comparison values based on operators + const assignedSdkVersion = this.calculateAssignedSdkVersion(operator, comparisonValue, apiPosition); + + // Handle OpenHarmony runtime with direct comparison + + if (runtimeType === this.openSourceRuntime) { + return this.sdkOpenSourceComparison(assignedSdkVersion); + } + + // Handle other runtime with version validation function + const versionChecker = getVersionValidationFunction(); + const triggerScene = matchedApi === this.openSourceDeviceInfo ? 1 : 2; + + const validationResult = versionChecker(this.minSinceVersion, assignedSdkVersion.toString(), triggerScene); + return validationResult.result; + } + + /** + * Compares SDK version using value assignment logic to determine if version check is valid. + * + * Logic: Determines what value the SDK version would have based on the condition, + * then compares that assigned value with the minimum required version. + * + * Examples: + * - sdkVersion > 15: SDK gets value 16, compare 16 >= minSince + * - sdkVersion >= 16: SDK gets value 16, compare 16 >= minSince + * - sdkVersion < 16: SDK gets value 15, compare 15 >= minSince + * - 16 < sdkVersion: SDK gets value 17, compare 17 >= minSince (flipped logic) + * + * @param assignedSdkVersion - The value being compared against in the condition + * @returns True if the assigned SDK version meets the minimum requirement + */ + private sdkOpenSourceComparison( + assignedSdkVersion: number + ): boolean { + const minRequiredVersion = Number(this.minSinceVersion); + + if (!Number.isInteger(minRequiredVersion) || assignedSdkVersion === null) { + return false; + } + + // Compare the assigned SDK version with minimum requirement + return assignedSdkVersion >= minRequiredVersion; + } + + /** + * Calculates what value the SDK version would have based on the comparison condition. + * + * @param operator - Comparison operator + * @param comparisonValue - Value being compared against + * @param apiPosition - Whether API is on left or right side + * @returns Assigned SDK version value, or null if indeterminate + */ + private calculateAssignedSdkVersion( + operator: string, + comparisonValue: number, + apiPosition: 'left' | 'right' + ): number | null { + // Flip operator if API is on the right side + const effectiveOperator = apiPosition === 'right' ? this.flipOperator(operator) : operator; + + switch (effectiveOperator) { + case '>': + // sdkVersion > 15 → SDK version would be at least 16 + return comparisonValue + 1; + + case '>=': + // sdkVersion >= 16 → SDK version would be at least 16 + return comparisonValue; + + case '<': + // sdkVersion < 16 → SDK version would be at most 15 + return comparisonValue - 1; + + case '<=': + // sdkVersion <= 15 → SDK version would be at most 15 + return comparisonValue; + + case '==': + case '===': + // sdkVersion === 16 → SDK version would be exactly 16 + return comparisonValue; + + case '!=': + case '!==': + // sdkVersion !== 15 → Cannot determine specific value + return null; + + default: + return null; + } + } + + /** + * Flips comparison operators for when API is on the right side of comparison. + * + * @param operator - Original operator + * @returns Flipped operator + */ + private flipOperator(operator: string): string { + switch (operator) { + case '>': return '<'; + case '<': return '>'; + case '>=': return '<='; + case '<=': return '>='; + case '==': + case '===': + case '!=': + case '!==': + return operator; // These don't need flipping + default: + return operator; + } + } + + private findValidImportApiIdentifier(expression: ts.Expression, api: string): ts.Identifier | undefined { + if (ts.isBinaryExpression(expression)) { + return this.extractApiIdentifierFromExpression(expression.left, api) || + this.extractApiIdentifierFromExpression(expression.right, api); + } + return this.extractApiIdentifierFromExpression(expression, api); + } + + private extractApiIdentifierFromExpression( + expression: ts.Expression, + targetProperty: string + ): ts.Identifier | undefined { + if (!ts.isPropertyAccessExpression(expression)) { + return undefined; + } + if (expression.name.text !== targetProperty) { + return undefined; + } + return this.getRootIdentifier(expression.expression); + } + + private getRootIdentifier(expression: ts.Expression): ts.Identifier | undefined { + let current: ts.Expression = expression; + while (ts.isPropertyAccessExpression(current)) { + current = current.expression; + } + return ts.isIdentifier(current) ? current : undefined; + } + + private isValidSdkDeclaration(identifier: ts.Identifier, validPackagePaths: string[]): boolean { + if (!this.typeChecker) { + return false; + } + const symbol = this.typeChecker.getSymbolAtLocation(identifier); + if (!symbol) { + return false; + } + const declarationFile = this.getActualDeclarationFile(symbol); + return declarationFile ? + this.isValidSdkDeclarationPath(declarationFile, validPackagePaths) : + false; + } + + private getActualDeclarationFile(symbol: ts.Symbol): string | undefined { + if (!this.typeChecker) { + return undefined; + } + const targetSymbol = this.typeChecker.getAliasedSymbol(symbol); + const actualSymbol = targetSymbol !== symbol ? targetSymbol : symbol; + if (!actualSymbol.declarations?.length) { + return undefined; + } + const declarationFile = actualSymbol.declarations[0].getSourceFile().fileName; + + // This regex removes the leading path up to and including the last "sdk//" or "sdk\\" + // before the "@", leaving only the part starting from the package name. + return declarationFile.replace(/^.*sdk[\\/].*[\\/](?=@)/, ''); + } + + private isValidSdkDeclarationPath(filePath: string, validPackagePaths: string[]): boolean { + const normalizedPath = this.normalizePath(filePath); + return validPackagePaths.some(validPath => + normalizedPath.includes(this.normalizePath(validPath)) + ); + } + + private normalizePath(path: string): string { + return path.replace(/\\/g, '/').toLowerCase(); + } + + private isNodeInIfThenBlock(node: ts.Node, ifStatement: ts.IfStatement): boolean { + if (!ifStatement.thenStatement) { + return false; + } + + const nodeStart = node.getStart(); + const nodeEnd = node.getEnd(); + const thenStart = ifStatement.thenStatement.getStart(); + const thenEnd = ifStatement.thenStatement.getEnd(); + const isInRange = nodeStart >= thenStart && nodeEnd <= thenEnd; + return isInRange; + } + + /** + * Traverses upward in the AST from the given node to find the first parent + * that satisfies the provided predicate function. + * + * @param node - The starting AST node. + * @param predicate - A function that returns `true` for the desired parent node. + * @returns The first matching parent node, or `null` if none is found. + */ + private findParentNode( + node: ts.Node, + predicate: (parent: ts.Node) => boolean + ): ts.Node | null { + let currentNode = node.parent; + + // Walk up the AST until we reach the root or find a match + while (currentNode) { + if (predicate(currentNode)) { + return currentNode; + } + currentNode = currentNode.parent; + } + return null; + } + + private getPrimaryNameFromNode(node: ts.Node): string | undefined { + if (ts.isIdentifier(node)) { + return node.text; + } + if (ts.isCallExpression(node)) { + return this.getPrimaryNameFromNode(node.expression); + } + if (ts.isPropertyAccessExpression(node)) { + return node.name.text; + } + return undefined; + } + + private isTargetNode(node: ts.Node, name: string): boolean { + const nodePrimaryName = this.getPrimaryNameFromNode(node); + return nodePrimaryName === name; + } +} diff --git a/compiler/src/interop/main.js b/compiler/src/interop/main.js index 36cbe76f28673da6b9afabad92ea85eac42b6b16..c76d06e6092707d88a4fea3cb1c089d05a6b2dfa 100644 --- a/compiler/src/interop/main.js +++ b/compiler/src/interop/main.js @@ -44,14 +44,14 @@ const { } = require('log4js'); const { - entryFileLanguageInfo, + setEntryFileLanguage, isMixCompile, processAbilityPagesFullPath, transformAbilityPages } = require('./lib/fast_build/ark_compiler/interop/interop_manager'); const { - ARKTS_1_2 + ARKTS_MODE } = require('./lib/fast_build/ark_compiler/interop/pre_define'); configure({ @@ -123,6 +123,8 @@ function initProjectConfig(projectConfig) { projectConfig.allowEmptyBundleName = false; projectConfig.uiTransformOptimization = false; projectConfig.ignoreCrossplatformCheck = false; + projectConfig.isolatedDeclarations = false; + projectConfig.noCheck = false; } function initProjectPathConfig(projectConfig) { @@ -505,16 +507,16 @@ function readAbilityEntrance(moduleJson) { if (moduleJson.module) { const moduleSrcEntrance = moduleJson.module.srcEntrance; const moduleSrcEntry = moduleJson.module.srcEntry; - const isStatic = moduleJson.module?.abilityStageCodeLanguage === ARKTS_1_2; + const isStatic = moduleJson.module?.arkTSMode === ARKTS_MODE.STATIC; if (moduleSrcEntry) { abilityPages.push(moduleSrcEntry); abilityPagesFullPath.add(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntry)); - entryFileLanguageInfo.set(moduleSrcEntry, isStatic); + setEntryFileLanguage(moduleSrcEntry, isStatic); } else if (moduleSrcEntrance) { abilityPages.push(moduleSrcEntrance); abilityPagesFullPath.add(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntrance)); - entryFileLanguageInfo.set(moduleSrcEntrance, isStatic); + setEntryFileLanguage(moduleSrcEntrance, isStatic); } if (moduleJson.module.abilities && moduleJson.module.abilities.length > 0) { setEntrance(moduleJson.module.abilities, abilityPages); @@ -530,14 +532,14 @@ function readAbilityEntrance(moduleJson) { function setEntrance(abilityConfig, abilityPages) { if (abilityConfig && abilityConfig.length > 0) { abilityConfig.forEach(ability => { - const isStatic = ability.codeLanguage === ARKTS_1_2; + const isStatic = ability.arkTSMode === ARKTS_MODE.STATIC; if (ability.srcEntry) { abilityPages.push(ability.srcEntry); - entryFileLanguageInfo.set(ability.srcEntry, isStatic); + setEntryFileLanguage(ability.srcEntry, isStatic); abilityPagesFullPath.add(getAbilityFullPath(projectConfig.projectPath, ability.srcEntry)); } else if (ability.srcEntrance) { abilityPages.push(ability.srcEntrance); - entryFileLanguageInfo.set(ability.srcEntrance, isStatic); + setEntryFileLanguage(ability.srcEntrance, isStatic); abilityPagesFullPath.add(getAbilityFullPath(projectConfig.projectPath, ability.srcEntrance)); } }); diff --git a/compiler/src/interop/src/ets_checker.ts b/compiler/src/interop/src/ets_checker.ts index e1d5381b33b6c10d13b7e985ec997e2fda472c1a..e3318c32cecf8beebc82a564017d432cf3eafde9 100644 --- a/compiler/src/interop/src/ets_checker.ts +++ b/compiler/src/interop/src/ets_checker.ts @@ -123,9 +123,11 @@ export interface LanguageServiceCache { service?: ts.LanguageService; pkgJsonFileHash?: string; targetESVersion?: ts.ScriptTarget; + types?: string[]; maxFlowDepth?: number; preTsImportSendable?: boolean; preSkipOhModulesLint?: boolean; + preEnableStrictCheckOHModule?: boolean; preMixCompile?: boolean; } @@ -193,7 +195,8 @@ function setCompilerOptions(resolveModulePaths: string[]): void { if (isMixCompile()) { compilerOptions.preserveValueImports = true; } - const suffix: string = projectConfig.hotReload ? HOT_RELOAD_BUILD_INFO_SUFFIX : TS_BUILD_INFO_SUFFIX; + const suffix: string = projectConfig?.hotReload ? HOT_RELOAD_BUILD_INFO_SUFFIX : + `${TS_BUILD_INFO_SUFFIX}${projectConfig?.widgetCompile === 'true' ? '_widget' : ''}`; const buildInfoPath: string = path.resolve(projectConfig.cachePath, '..', suffix); checkArkTSVersion(); Object.assign(compilerOptions, { @@ -226,7 +229,10 @@ function setCompilerOptions(resolveModulePaths: string[]): void { 'compatibleSdkVersionStage': projectConfig.compatibleSdkVersionStage, 'compatibleSdkVersion': projectConfig.compatibleSdkVersion, 'skipOhModulesLint': skipOhModulesLint, - 'mixCompile': mixCompile + 'enableStrictCheckOHModule': enableStrictCheckOHModule, + 'mixCompile': mixCompile, + 'isCompileJsHar': isCompileJsHar(), + 'moduleRootPath': projectConfig.moduleRootPath, }); if (projectConfig.compileMode === ESMODULE) { Object.assign(compilerOptions, { @@ -240,6 +246,11 @@ function setCompilerOptions(resolveModulePaths: string[]): void { readTsBuildInfoFileInCrementalMode(buildInfoPath, projectConfig); } +function isCompileJsHar(): boolean { + return projectConfig.compileHar && projectConfig.byteCodeHar === false && projectConfig.buildMode === 'Release' && + projectConfig.obfuscationOptions?.selfConfig.ruleOptions.enable && !projectConfig.useTsHar; +} + function checkArkTSVersion(): void { const etsCheckerLogger = fastBuildLogger || logger; if (getArkTSVersion() === ArkTSVersion.ArkTS_1_0 && tsImportSendable) { @@ -457,30 +468,31 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi let service: ts.LanguageService | undefined = cache?.service; const currentHash: string | undefined = rollupShareObject?.projectConfig?.pkgJsonFileHash; const currentTargetESVersion: ts.ScriptTarget = compilerOptions.target; + const currentTypes: string[] | undefined = compilerOptions.types; const currentMaxFlowDepth: number | undefined = compilerOptions.maxFlowDepth; const lastHash: string | undefined = cache?.pkgJsonFileHash; const lastTargetESVersion: ts.ScriptTarget | undefined = cache?.targetESVersion; + const lastTypes: string[] | undefined = cache?.types; const lastMaxFlowDepth: number | undefined = cache?.maxFlowDepth; const hashDiffers: boolean | undefined = currentHash && lastHash && currentHash !== lastHash; const shouldRebuildForDepDiffers: boolean | undefined = reuseLanguageServiceForDepChange ? (hashDiffers && !rollupShareObject?.depInfo?.enableIncre) : hashDiffers; const targetESVersionDiffers: boolean | undefined = lastTargetESVersion && currentTargetESVersion && lastTargetESVersion !== currentTargetESVersion; + const typesDiff: boolean | undefined = lastTypes && currentTypes && !areEqualArrays(lastTypes, currentTypes); const maxFlowDepthDiffers: boolean | undefined = lastMaxFlowDepth && currentMaxFlowDepth && lastMaxFlowDepth !== currentMaxFlowDepth; - const tsImportSendableDiff: boolean = (cache?.preTsImportSendable === undefined && !tsImportSendable) ? - false : - cache?.preTsImportSendable !== tsImportSendable; - const skipOhModulesLintDiff: boolean = (cache?.preSkipOhModulesLint === undefined && !skipOhModulesLint) ? - false : cache?.preSkipOhModulesLint !== skipOhModulesLint; - const mixCompileDiff: boolean = (cache?.preMixCompile === undefined && !mixCompile) ? - false : cache?.preMixCompile !== mixCompile; - const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || - tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff; + const tsImportSendableDiff: boolean = checkValueDiff(cache?.preTsImportSendable, tsImportSendable); + const skipOhModulesLintDiff: boolean = checkValueDiff(cache?.preSkipOhModulesLint, skipOhModulesLint); + const enableStrictCheckOHModuleDiff: boolean = checkValueDiff(cache?.preEnableStrictCheckOHModule, enableStrictCheckOHModule); + const mixCompileDiff: boolean = checkValueDiff(cache?.preMixCompile, mixCompile); + const onlyDeleteBuildInfoCache: boolean | undefined = tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || + enableStrictCheckOHModuleDiff || mixCompileDiff || typesDiff; + const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || onlyDeleteBuildInfoCache; if (reuseLanguageServiceForDepChange && hashDiffers && rollupShareObject?.depInfo?.enableIncre) { needReCheckForChangedDepUsers = true; } if (!service || shouldRebuild) { - rebuildProgram(targetESVersionDiffers, tsImportSendableDiff, maxFlowDepthDiffers, skipOhModulesLintDiff, mixCompileDiff); + rebuildProgram(targetESVersionDiffers, onlyDeleteBuildInfoCache); service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); } else { // Found language service from cache, update root files @@ -492,6 +504,7 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi service: service, pkgJsonFileHash: currentHash, targetESVersion: currentTargetESVersion, + types: currentTypes, maxFlowDepth: currentMaxFlowDepth, preTsImportSendable: tsImportSendable, preSkipOhModulesLint: skipOhModulesLint, @@ -501,14 +514,43 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi return service; } -function rebuildProgram(targetESVersionDiffers: boolean | undefined, tsImportSendableDiff: boolean, - maxFlowDepthDiffers: boolean | undefined, skipOhModulesLintDiff: boolean, mixCompileDiff: boolean): void { +/** + * compare cache value and current value, check if they are different + * @param cacheValue cache value + * @param currentValue current value + * @returns true if they are different, false otherwise + */ +function checkValueDiff(cacheValue: boolean | undefined, currentValue: boolean): boolean { + return !(cacheValue === undefined && !currentValue) && cacheValue !== currentValue; +} + +export function areEqualArrays(lastArray: string[] | undefined, currentArray: string[] | undefined): boolean { + if (!lastArray || !currentArray) { + return lastArray === currentArray; + } + + const currentSet: Set = new Set(currentArray!); + const lastSet: Set = new Set(lastArray!); + + if (lastSet.size !== currentSet.size) { + return false; + } + + for (const item of lastSet) { + if (!currentSet.has(item)) { + return false; + } + } + return true; +} + +function rebuildProgram(targetESVersionDiffers: boolean | undefined, onlyDeleteBuildInfoCache: boolean | undefined): void { if (targetESVersionDiffers) { // If the targetESVersion is changed, we need to delete the build info cahce files deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); targetESVersionChanged = true; - } else if (tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff) { - // When tsImportSendable or maxFlowDepth is changed, we need to delete the build info cahce files + } else if (onlyDeleteBuildInfoCache) { + // When tsImportSendable or types or maxFlowDepth or skipOhModuleslint or mixCompile is changed, we need to delete the build info cahce files deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); } } @@ -568,6 +610,7 @@ export const warnCheckerResult: WarnCheckerResult = { count: 0 }; export let languageService: ts.LanguageService = null; let tsImportSendable: boolean = false; let skipOhModulesLint: boolean = false; +let enableStrictCheckOHModule: boolean = false; let mixCompile: boolean = false; export let maxMemoryInServiceChecker: number = 0; export function serviceChecker(rootFileNames: string[], newLogger: Object = null, resolveModulePaths: string[] = null, @@ -576,6 +619,7 @@ export function serviceChecker(rootFileNames: string[], newLogger: Object = null let cacheFile: string = null; tsImportSendable = rollupShareObject?.projectConfig.tsImportSendable; skipOhModulesLint = rollupShareObject?.projectConfig.skipOhModulesLint; + enableStrictCheckOHModule = rollupShareObject?.projectConfig.enableStrictCheckOHModule; mixCompile = rollupShareObject?.projectConfig.mixCompile; if (projectConfig.xtsMode || process.env.watchMode === 'true') { if (projectConfig.hotReload) { @@ -588,7 +632,8 @@ export function serviceChecker(rootFileNames: string[], newLogger: Object = null MemoryMonitor.stopRecordStage(recordInfo); props = languageService.getProps(); } else { - cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache'); + cacheFile = rollupShareObject?.projectConfig?.widgetCompile === 'true' ? path.resolve(projectConfig.cachePath, '../.ts_checker_cache_widget') : + path.resolve(projectConfig.cachePath, '../.ts_checker_cache'); const [isJsonObject, cacheJsonObject]: [boolean, WholeCache | undefined] = isJsonString(cacheFile); const wholeCache: WholeCache = isJsonObject ? cacheJsonObject : { 'runtimeOS': projectConfig.runtimeOS, 'sdkInfo': projectConfig.sdkInfo, 'fileList': {} }; if (wholeCache.runtimeOS === projectConfig.runtimeOS && wholeCache.sdkInfo === projectConfig.sdkInfo) { @@ -786,7 +831,7 @@ function processBuildHap(cacheFile: string, rootFileNames: string[], parentEvent if ((/\.d\.e?ts$/).test(moduleFile)) { generateSourceFilesInHar(moduleFile, fs.readFileSync(moduleFile, 'utf-8'), path.extname(moduleFile), projectConfig, projectConfig.modulePathMap); - } else if ((/\.e?ts$/).test(moduleFile)) { + } else if ((/\.e?ts$/).test(moduleFile) && !toUnixPath(moduleFile).includes('/oh_modules/')) { emit = undefined; let sourcefile = globalProgram.program.getSourceFile(moduleFile); if (sourcefile) { @@ -804,8 +849,16 @@ function processBuildHap(cacheFile: string, rootFileNames: string[], parentEvent } function printDeclarationDiagnostics(errorCodeLogger?: Object | undefined): void { - globalProgram.builderProgram.getDeclarationDiagnostics().forEach((diagnostic: ts.Diagnostic) => { - printDiagnostic(diagnostic, ErrorCodeModule.TSC, errorCodeLogger); + globalProgram.program.getSourceFiles().forEach((sourceFile: ts.SourceFile) => { + if ((/\.d\.e?ts$/).test(sourceFile.fileName) || (/\.js$/).test(sourceFile.fileName)) { + return; + } + if (toUnixPath(sourceFile.fileName).includes('/oh_modules/')) { + return; + } + globalProgram.builderProgram.getDeclarationDiagnostics(sourceFile).forEach((diagnostic: ts.Diagnostic) => { + printDiagnostic(diagnostic, ErrorCodeModule.TSC, errorCodeLogger); + }); }); } @@ -1919,8 +1972,6 @@ export function etsStandaloneChecker(entryObj, logger, projectConfig): void { export function resetEtsCheckTypeScript(): void { if (globalProgram.program) { globalProgram.program.releaseTypeChecker(); - } else if (languageService) { - languageService.getProgram().releaseTypeChecker(); } resetGlobalProgram(); languageService = null; diff --git a/compiler/src/interop/src/fast_build/ark_compiler/generate_sourcemap.ts b/compiler/src/interop/src/fast_build/ark_compiler/generate_sourcemap.ts index a8549f003f1d56b3735a32e91a7fff86cff6ad0e..6453b2490138371319b3bcf91a6db7e48239b71f 100644 --- a/compiler/src/interop/src/fast_build/ark_compiler/generate_sourcemap.ts +++ b/compiler/src/interop/src/fast_build/ark_compiler/generate_sourcemap.ts @@ -81,8 +81,8 @@ export class SourceMapGenerator { private logger: CommonLogger; private isFirstAppend: boolean = true; private isCompileSingle: boolean = false; - private originFd: number; - private tempFd: number; + private originFileEdited: boolean = false; + private tempFileEdited: boolean = false; public sourceMapKeyMappingForObf: Map = new Map(); @@ -227,7 +227,7 @@ export class SourceMapGenerator { } public writeOrigin(content: string): void { - if (!this.originFd) { + if (!this.originFileEdited) { if (fs.existsSync(this.sourceMapPath)) { fs.unlinkSync(this.sourceMapPath); } @@ -235,13 +235,13 @@ export class SourceMapGenerator { if (!fs.existsSync(sourceMapPathDir)) { fs.mkdirSync(sourceMapPathDir, { recursive: true }); } - this.originFd = fs.openSync(this.sourceMapPath, 'a'); + this.originFileEdited = true; } - fs.appendFileSync(this.originFd, content, { encoding: 'utf8' }); + fs.appendFileSync(this.sourceMapPath, content, 'utf8'); } public writeTemp(content: string): void { - if (!this.tempFd) { + if (!this.tempFileEdited) { if (fs.existsSync(this.sourceMapPathTmp)) { fs.unlinkSync(this.sourceMapPathTmp); } @@ -249,20 +249,14 @@ export class SourceMapGenerator { if (!fs.existsSync(sourceMapPathTmpDir)) { fs.mkdirSync(sourceMapPathTmpDir, { recursive: true }); } - this.tempFd = fs.openSync(this.sourceMapPathTmp, 'a'); + this.tempFileEdited = true; } - fs.appendFileSync(this.tempFd, content, { encoding: 'utf8' }); + fs.appendFileSync(this.sourceMapPathTmp, content, 'utf8'); } - public closeFd(): void { - if (this.originFd) { - fs.closeSync(this.originFd); - this.originFd = undefined; - } - if (this.tempFd) { - fs.closeSync(this.tempFd); - this.tempFd = undefined; - } + public resetFileEdited(): void { + this.originFileEdited = false; + this.tempFileEdited = false; } public convertSourceMapToCache(maps: Object): string { @@ -318,7 +312,7 @@ export class SourceMapGenerator { if (!fs.existsSync(this.sourceMapPathTmp)) { this.writeTemp(''); } - this.closeFd(); + this.resetFileEdited(); if (fs.existsSync(this.sourceMapForMergePath)) { fs.unlinkSync(this.sourceMapForMergePath); } @@ -637,7 +631,7 @@ export class SourceMapGenerator { public static cleanSourceMapObject(): void { if (this.instance) { - this.instance.closeFd(); + this.instance.resetFileEdited(); this.instance.keyCache.clear(); this.instance.sourceMaps = undefined; this.instance = undefined; diff --git a/compiler/src/interop/src/fast_build/ark_compiler/interop/interop_manager.ts b/compiler/src/interop/src/fast_build/ark_compiler/interop/interop_manager.ts index 3fe736afd4403ffe6e8ef43e1a952537263f39c0..4ec3efcc8d9dee4c0057eb1c10c37528a0fe2a38 100644 --- a/compiler/src/interop/src/fast_build/ark_compiler/interop/interop_manager.ts +++ b/compiler/src/interop/src/fast_build/ark_compiler/interop/interop_manager.ts @@ -17,14 +17,23 @@ import fs from 'fs'; import path from 'path'; import { + globalModulePaths, + initBuildInfo, + loadEntryObj, + loadModuleInfo, + loadWorker, projectConfig, + readAppResource, + readPatchConfig, + readWorkerFile, sdkConfigs } from '../../../../main'; import { toUnixPath } from '../../../utils'; import { ArkTSEvolutionModule, FileInfo, - AliasConfig + AliasConfig, + InteropConfig } from './type'; import { hasExistingPaths, @@ -46,8 +55,15 @@ import { ARKTS_1_2, ARKTS_HYBRID } from './pre_define'; +import { readFirstLineSync } from './utils'; -export let entryFileLanguageInfo = new Map(); +let entryFileLanguageInfo = new Map(); +export let workerFile = null; +export let mixCompile = undefined; + +export function setEntryFileLanguage(filePath: string, language: string): void { + entryFileLanguageInfo.set(filePath, language); +} export class FileManager { private static instance: FileManager | undefined = undefined; @@ -60,7 +76,7 @@ export class FileManager { static mixCompile: boolean = false; static glueCodeFileInfos: Map = new Map(); static isInteropSDKEnabled: boolean = false; - static sharedObj: Object | undefined = undefined; + interopConfig: InteropConfig | undefined = undefined; private constructor() { } @@ -101,14 +117,18 @@ export class FileManager { return FileManager.instance; } - public static setRollUpObj(shared: Object): void { - FileManager.sharedObj = shared; - } - public static setMixCompile(mixCompile: boolean): void { FileManager.mixCompile = mixCompile; } + public setInteropConfig(interopConfig: InteropConfig): void { + this.interopConfig = interopConfig; + } + + public getInteropConfig(): InteropConfig { + return this.interopConfig; + } + private static initLanguageVersionFromDependentModuleMap( dependentModuleMap: Map ): void { @@ -217,7 +237,6 @@ export class FileManager { FileManager.glueCodeFileInfos?.clear(); FileManager.aliasConfig?.clear(); FileManager.mixCompile = false; - entryFileLanguageInfo.clear(); } getLanguageVersionByFilePath(filePath: string): { @@ -302,11 +321,7 @@ export class FileManager { } private static logError(error: LogData): void { - if (FileManager.sharedObj) { - CommonLogger.getInstance(FileManager.sharedObj).printErrorAndExit(error); - } else { - console.error(error.toString()); - } + console.error(error.toString()); } private static matchSDKPath(path: string): { @@ -369,22 +384,22 @@ export class FileManager { } } -export function initFileManagerInRollup(share: Object): void { - if (!share.projectConfig.mixCompile) { +export function initFileManagerInRollup(InteropConfig: InteropConfig): void { + if (!isMixCompile()) { return; } FileManager.mixCompile = true; - const sdkInfo = collectSDKInfo(share); + const sdkInfo = collectSDKInfo(InteropConfig); FileManager.init( - share.projectConfig.dependentModuleMap, - share.projectConfig.aliasPaths, + InteropConfig.projectConfig.dependentModuleMap, + InteropConfig.projectConfig.aliasPaths, sdkInfo.dynamicSDKPath, sdkInfo.staticSDKInteropDecl, sdkInfo.staticSDKGlueCodePath ); - FileManager.setRollUpObj(share); + FileManager.getInstance().setInteropConfig(InteropConfig); } export function collectSDKInfo(share: Object): { @@ -433,15 +448,6 @@ export function collectSDKInfo(share: Object): { }; } -function readFirstLineSync(filePath: string): string { - const buffer = fs.readFileSync(filePath, 'utf-8'); - const newlineIndex = buffer.indexOf('\n'); - if (newlineIndex === -1) { - return buffer.trim(); - } - return buffer.substring(0, newlineIndex).trim(); -} - export function isBridgeCode(filePath: string, projectConfig: Object): boolean { if (!projectConfig?.mixCompile) { return false; @@ -455,6 +461,9 @@ export function isBridgeCode(filePath: string, projectConfig: Object): boolean { } export function isMixCompile(): boolean { + if (typeof mixCompile === 'boolean') { + return mixCompile; + } return process.env.mixCompile === 'true'; } @@ -493,7 +502,7 @@ export function processAbilityPagesFullPath(abilityPagesFullPath: Set): export function transformAbilityPages(abilityPath: string): boolean { - const entryBridgeCodePath = process.env.entryBridgeCodePath; + const entryBridgeCodePath = getBrdigeCodeRootPath(abilityPath, FileManager.getInstance().getInteropConfig()); if (!entryBridgeCodePath) { const errInfo = LogDataFactory.newInstance( ErrorCode.ETS2BUNDLE_INTERNAL_MISSING_BRIDGECODE_PATH_INFO, @@ -516,9 +525,12 @@ export function transformAbilityPages(abilityPath: string): boolean { return false; } -function transformModuleNameToRelativePath(moduleName): string { +export function transformModuleNameToRelativePath(filePath: string): string { let defaultSourceRoot = 'src/main'; - const normalizedModuleName = moduleName.replace(/\\/g, '/'); + if (FileManager.getInstance().getInteropConfig()?.projectConfig?.isOhosTest) { + defaultSourceRoot = 'src/ohosTest'; + } + const normalizedModuleName = filePath.replace(/\\/g, '/'); const normalizedRoot = defaultSourceRoot.replace(/\\/g, '/'); const rootIndex = normalizedModuleName.indexOf(`/${normalizedRoot}/`); @@ -527,15 +539,16 @@ function transformModuleNameToRelativePath(moduleName): string { ErrorCode.ETS2BUNDLE_INTERNAL_WRONG_MODULE_NAME_FROM_ACEMODULEJSON, ArkTSInternalErrorDescription, `defaultSourceRoot '${defaultSourceRoot}' not found ` + - `when process moduleName '${moduleName}'` + `when process moduleName '${filePath}'` ); throw Error(errInfo.toString()); } - const relativePath = normalizedModuleName.slice(rootIndex + normalizedRoot.length + 1); + const relativePath = normalizedModuleName.slice(rootIndex + normalizedRoot.length + 1).replace(/^\/+/, ''); return './' + relativePath; } + export function getApiPathForInterop(apiDirs: string[], languageVersion: string): void { if (languageVersion !== ARKTS_1_2) { return; @@ -545,7 +558,7 @@ export function getApiPathForInterop(apiDirs: string[], languageVersion: string) apiDirs.unshift(...staticPaths); } -export function rebuildEntryObj(projectConfig: Object): void { +export function rebuildEntryObj(projectConfig: Object, interopConfig: InteropConfig): void { const entryObj = projectConfig.entryObj; const removeExt = (p: string): string => p.replace(/\.[^/.]+$/, ''); @@ -562,7 +575,7 @@ export function rebuildEntryObj(projectConfig: Object): void { if (!firstLine.includes('use static')) { newEntry[newKey] = rawPath; } else if (rawPath.startsWith(projectConfig.projectRootPath)) { - const bridgePath = process.env.entryBridgeCodePath; + const bridgePath = getBrdigeCodeRootPath(rawPath, interopConfig); if (!bridgePath) { const errInfo = LogDataFactory.newInstance( ErrorCode.ETS2BUNDLE_INTERNAL_MISSING_BRIDGECODE_PATH_INFO, @@ -580,3 +593,64 @@ export function rebuildEntryObj(projectConfig: Object): void { return newEntry; }, {} as Record); } + + +/** + * corresponds to compiler/src/fast_build/common/init_config.ts - initConfig() + * As the entry for mix compile,so mixCompile status will be set true + */ +export function initConfigForInterop(interopConfig: InteropConfig): Object { + initFileManagerInRollup(interopConfig); + + function getEntryObj(): void { + loadEntryObj(projectConfig); + initBuildInfo(); + readPatchConfig(); + loadModuleInfo(projectConfig); + workerFile = readWorkerFile(); + if (!projectConfig.isPreview) { + loadWorker(projectConfig, workerFile); + } + if (isMixCompile()) { + rebuildEntryObj(projectConfig, interopConfig); + return; + } + projectConfig.entryObj = Object.keys(projectConfig.entryObj).reduce((newEntry, key) => { + const newKey: string = key.replace(/^\.\//, ''); + newEntry[newKey] = projectConfig.entryObj[key].replace('?entry', ''); + return newEntry; + }, {}); + } + mixCompile = true; + getEntryObj(); + if (process.env.appResource) { + readAppResource(process.env.appResource); + } + return { + entryObj: Object.assign({}, projectConfig.entryObj, projectConfig.otherCompileFiles), + cardEntryObj: projectConfig.cardEntryObj, + workerFile: workerFile, + globalModulePaths: globalModulePaths + }; +} + +export function getBrdigeCodeRootPath(filePath: string, interopConfig: InteropConfig): string | undefined { + if (!interopConfig) { + return process.env.entryBridgeCodePath; + } + + for (const [moduleRootPath, InteropInfo] of interopConfig.interopModuleInfo) { + if (isSubPathOf(filePath, moduleRootPath)) { + return InteropInfo.declgenBridgeCodePath; + } + } + + return undefined; +} + +export function destroyInterop(): void { + FileManager.cleanFileManagerObject(); + entryFileLanguageInfo.clear(); + mixCompile = false; +} + diff --git a/compiler/src/interop/src/fast_build/ark_compiler/interop/pre_define.ts b/compiler/src/interop/src/fast_build/ark_compiler/interop/pre_define.ts index eaa908d4149f56a25a56bee5b66a7200743d75d5..82aaebb55ce173949d74fc49d0930e0e0b16a2b5 100644 --- a/compiler/src/interop/src/fast_build/ark_compiler/interop/pre_define.ts +++ b/compiler/src/interop/src/fast_build/ark_compiler/interop/pre_define.ts @@ -16,4 +16,11 @@ export const ARKTS_1_2: string = '1.2'; export const ARKTS_1_1: string = '1.1'; export const ARKTS_1_0: string = '1.0'; -export const ARKTS_HYBRID: string = 'hybrid'; \ No newline at end of file +export const ARKTS_HYBRID: string = 'hybrid'; + +export const DECLGEN_CACHE_FILE = 'declgen_cache.json'; + +export enum ARKTS_MODE { + STATIC = 'static', + DYNAMIC = 'dynamic' +} \ No newline at end of file diff --git a/compiler/src/interop/src/fast_build/ark_compiler/interop/type.ts b/compiler/src/interop/src/fast_build/ark_compiler/interop/type.ts index 3a3dc2a11c4c1e7e197241e5f46c4bcaa55c8624..cb4b3a29ea850cd50e2b8e6bab823639be24949f 100644 --- a/compiler/src/interop/src/fast_build/ark_compiler/interop/type.ts +++ b/compiler/src/interop/src/fast_build/ark_compiler/interop/type.ts @@ -112,4 +112,13 @@ export interface FileInfo { baseUrl: string; abstractPath: string; } -export const DECLGEN_CACHE_FILE = 'declgen_cache.json'; + +export interface InteropInfo { + declgenBridgeCodePath: string; + declgenV1OutPath: string; +} + +export interface InteropConfig { + interopModuleInfo: Map; + projectConfig: Object; +} diff --git a/compiler/src/interop/src/fast_build/ark_compiler/interop/utils.ts b/compiler/src/interop/src/fast_build/ark_compiler/interop/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..757c30f8d08628637e1ab8e830e4e9fb881e3e04 --- /dev/null +++ b/compiler/src/interop/src/fast_build/ark_compiler/interop/utils.ts @@ -0,0 +1,25 @@ +/* + * 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 fs from 'fs'; + +export function readFirstLineSync(filePath: string): string { + const buffer = fs.readFileSync(filePath, 'utf-8'); + const newlineIndex = buffer.indexOf('\n'); + if (newlineIndex === -1) { + return buffer.trim(); + } + return buffer.substring(0, newlineIndex).trim(); +} \ No newline at end of file diff --git a/compiler/src/interop/src/utils.ts b/compiler/src/interop/src/utils.ts index 7b0a287a9963c60b92aedf56941748867058c237..247f74696f6b13badf4d6c7fe082a9c34578d3db 100644 --- a/compiler/src/interop/src/utils.ts +++ b/compiler/src/interop/src/utils.ts @@ -1303,7 +1303,7 @@ export function removeDecorator(decorators: readonly ts.Decorator[], decoratorNa export function isFileInProject(filePath: string, projectRootPath: string): boolean { const relativeFilePath: string = toUnixPath(path.relative(toUnixPath(projectRootPath), toUnixPath(filePath))); // When processing ohmurl, hsp's filePath is consistent with moduleRequest - return fs.existsSync(filePath) && fs.statSync(filePath).isFile() && !relativeFilePath.startsWith('../'); + return path.isAbsolute(filePath) && fs.existsSync(filePath) && fs.statSync(filePath).isFile() && !relativeFilePath.startsWith('../'); } export function getProjectRootPath(filePath: string, projectConfig: Object, rootPathSet: Object): string { diff --git a/compiler/src/pre_define.ts b/compiler/src/pre_define.ts index 659f618b2fba39cacda6310276cc74e5581dd233..17a17436894f528acf11d9db873feb4adc773c5b 100644 --- a/compiler/src/pre_define.ts +++ b/compiler/src/pre_define.ts @@ -66,6 +66,12 @@ export const REUSABLE_V2_INNER_DECORATOR: string = '__ReusableV2_Inner_Decorator export const REUSE_ATTRIBUTE: string = 'reuse'; export const COMPONENT_USER_INTENTS_DECORATOR_PAGE: string = '@InsightIntentPage'; +export const COMPONENT_USER_INTENTS_DECORATOR_LINK: string = '@InsightIntentLink'; +export const COMPONENT_USER_INTENTS_DECORATOR_ENTRY: string = '@InsightIntentEntry'; +export const COMPONENT_USER_INTENTS_DECORATOR_FUNCTION: string = '@InsightIntentFunction'; +export const COMPONENT_USER_INTENTS_DECORATOR_METHOD: string = '@InsightIntentFunctionMethod'; +export const COMPONENT_USER_INTENTS_DECORATOR_ENTITY: string = '@InsightIntentEntity'; +export const COMPONENT_USER_INTENTS_DECORATOR_FORM: string = '@InsightIntentForm'; export const COMPONENT_DECORATORS_PARAMS: Set = new Set([COMPONENT_CONSUME_DECORATOR, COMPONENT_STORAGE_PROP_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR, COMPONENT_PROVIDE_DECORATOR, @@ -95,12 +101,6 @@ export const COMPONENT_STYLES_DECORATOR: string = '@Styles'; export const COMPONENT_ANIMATABLE_EXTEND_DECORATOR: string = '@AnimatableExtend'; export const COMPONENT_CONCURRENT_DECORATOR: string = '@Concurrent'; export const COMPONENT_SENDABLE_DECORATOR: string = '@Sendable'; -export const COMPONENT_USER_INTENTS_DECORATOR: string = '@InsightIntentLink'; -export const COMPONENT_USER_INTENTS_DECORATOR_ENTRY: string = '@InsightIntentEntry'; -export const COMPONENT_USER_INTENTS_DECORATOR_FUNCTION: string = '@InsightIntentFunction'; -export const COMPONENT_USER_INTENTS_DECORATOR_METHOD: string = '@InsightIntentFunctionMethod'; -export const COMPONENT_USER_INTENTS_DECORATOR_ENTITY: string = '@InsightIntentEntity'; -export const COMPONENT_USER_INTENTS_DECORATOR_FORM: string = '@InsightIntentForm'; export const CHECK_COMPONENT_EXTEND_DECORATOR: string = 'Extend'; export const STRUCT_CONTEXT_METHOD_DECORATORS: Set = new Set([COMPONENT_BUILDER_DECORATOR, COMPONENT_STYLES_DECORATOR, COMPONENT_LOCAL_BUILDER_DECORATOR]); diff --git a/compiler/src/process_component_class.ts b/compiler/src/process_component_class.ts index e39e30174a1c234ef8eb04e95fec81881914908d..160a4d911f73f1f39efa324771524550b2f8bf27 100644 --- a/compiler/src/process_component_class.ts +++ b/compiler/src/process_component_class.ts @@ -153,7 +153,8 @@ import { import { builderTypeParameter, initializeMYIDS, - globalBuilderParamAssignment + globalBuilderParamAssignment, + parseStylesNode } from './process_ui_syntax'; import constantDefine from './constant_define'; import processStructComponentV2, { StructInfo } from './process_struct_componentV2'; @@ -626,6 +627,7 @@ export function processComponentMethod(node: ts.MethodDeclaration, context: ts.T storedFileInfo.processBuilder = false; storedFileInfo.processLocalBuilder = false; } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { + parseStylesNode(node, log); if (node.parameters && node.parameters.length === 0) { if (ts.isBlock(node.body) && node.body.statements && node.body.statements.length) { INNER_STYLE_FUNCTION.set(name, node.body); diff --git a/compiler/src/process_ui_syntax.ts b/compiler/src/process_ui_syntax.ts index 68209e23dc5bf5b6cd2bd3dc930f66d4569402ff..7c9da6ef8dcba4ecdf835335d0af6643adc6f392 100644 --- a/compiler/src/process_ui_syntax.ts +++ b/compiler/src/process_ui_syntax.ts @@ -80,7 +80,8 @@ import { PAGE_FULL_PATH, LENGTH, PUV2_VIEW_BASE, - CONTEXT_STACK + CONTEXT_STACK, + CHECK_COMPONENT_EXTEND_DECORATOR } from './pre_define'; import { componentInfo, @@ -131,7 +132,8 @@ import { GLOBAL_STYLE_FUNCTION, INTERFACE_NODE_SET, ID_ATTRS, - GLOBAL_CUSTOM_BUILDER_METHOD + GLOBAL_CUSTOM_BUILDER_METHOD, + INNER_COMPONENT_NAMES } from './component_map'; import { resources, @@ -171,12 +173,12 @@ import { createAndStartEvent, stopEvent } from './performance'; +import parseIntent from './userIntents_parser/parseUserIntents'; export let transformLog: IFileLog = new createAstNodeUtils.FileLog(); export let contextGlobal: ts.TransformationContext; export let resourceFileName: string = ''; export const builderTypeParameter: { params: string[] } = { params: [] }; -import parseIntent from './userIntents_parser/parseUserIntents'; export function processUISyntax(program: ts.Program, ut = false, parentEvent?: CompileEvent, filePath: string = '', share: object = null, metaInfo: Object = {}): Function { @@ -354,6 +356,7 @@ export function processUISyntax(program: ts.Program, ut = false, storedFileInfo.processBuilder = false; storedFileInfo.processGlobalBuilder = false; } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { + parseStylesNode(node, transformLog.errors); if (node.parameters.length === 0) { node = undefined; } else { @@ -747,7 +750,7 @@ export function processResourceData(node: ts.CallExpression, filePath: string, const resourceData: string[] = (node.arguments[0] as ts.StringLiteral).text.trim().split('.'); const isResourceModule: boolean = resourceData.length && /^\[.*\]$/g.test(resourceData[0]); if (node.expression.getText() === RESOURCE_RAWFILE) { - isResourcefile(node, previewLog, isResourceModule, isTemplateString, isCorrectResources); + isResourcefile(node, previewLog, isResourceModule, isTemplateString, isCorrectResources, filePath); if (isCorrectResources.booleanValue) { resourcePreviewMessage(previewLog); return createResourceParamWithVariable(node, -1, RESOURCE_TYPE.rawfile); @@ -815,7 +818,7 @@ function getResourceDataNode(node: ts.CallExpression, previewLog: {isAccelerateP } function isResourcefile(node: ts.CallExpression, previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}, isResourceModule: boolean, - isTemplateString: boolean, isCorrectResources: isCorrectResourcesType): void { + isTemplateString: boolean, isCorrectResources: isCorrectResourcesType, filePath: string): void { if (!isResourceModule && process.env.rawFileResource && !storedFileInfo.resourcesArr.has(node.arguments[0].text) && !previewLog.isAcceleratePreview && process.env.compileMode === 'moduleJson') { isTemplateString && (isCorrectResources.booleanValue = true); @@ -825,6 +828,11 @@ function isResourcefile(node: ts.CallExpression, previewLog: {isAcceleratePrevie pos: node.getStart(), code: '10904333' }); + } else if (!isResourceModule && process.env.rawFileResource) { + if (!storedFileInfo.resourcesForFiles.has(filePath)) { + storedFileInfo.resourcesForFiles.set(filePath, []); + } + storedFileInfo.resourcesForFiles.get(filePath).push(node.arguments[0].text); } } @@ -1024,6 +1032,7 @@ export function processAnimateToOrImmediately(node: ts.CallExpression): ts.CallE function processExtend(node: ts.FunctionDeclaration, log: LogInfo[], decoratorName: string): ts.FunctionDeclaration { const componentName: string = isExtendFunction(node, { decoratorName: '', componentName: '' }, true); + checkExtendNode(node, componentName, log); if (componentName && node.body && !node.body.statements.length && decoratorName === COMPONENT_EXTEND_DECORATOR) { const statementArray: ts.Statement[] = []; const bodynode: ts.Block = ts.visitEachChild(node.body, traverseExtendExpression, contextGlobal); @@ -1277,12 +1286,86 @@ function parseExtendNode(node: ts.CallExpression, extendResult: ExtendResult, ch code: '10905108' }); } + if (checkArguments && CHECK_EXTEND_DECORATORS.includes(extendResult.decoratorName) && + node.arguments && node.arguments.length === 1 && ts.isIdentifier(node.arguments[0]) && + !INNER_COMPONENT_NAMES.has(node.arguments[0].getText())) { + transformLog.errors.push({ + type: LogType.WARN, + message: `'${node.arguments[0].getText()}' parameter cannot be` + + ` passed in the '@${extendResult.decoratorName}' decorator.`, + pos: node.getStart() + }); + } } if (node.arguments.length && ts.isIdentifier(node.arguments[0])) { extendResult.componentName = node.arguments[0].escapedText.toString(); } } +function checkExtendNode(node: ts.FunctionDeclaration, componentName: string, + log: LogInfo[]): void { + const componentInstance: string = `${componentName}Instance`; + if (node.body && ts.isBlock(node.body) && node.body.statements && + node.body.statements.length > 1) { + validateUIFunctionFormat(log, node.body.statements[0]); + return; + } + if (node.body && ts.isBlock(node.body) && node.body.statements && + node.body.statements.length === 1 && ts.isExpressionStatement(node.body.statements[0])) { + !validateComponentInstance(node.body.statements[0], componentInstance) && + validateUIFunctionFormat(log, node.body.statements[0]); + return; + } + if (node.body && ts.isBlock(node.body) && node.body.statements && + node.body.statements.length === 1 && !ts.isExpressionStatement(node.body.statements[0])) { + validateUIFunctionFormat(log, node.body.statements[0]); + return; + } +} + +function validateComponentInstance(node: ts.ExpressionStatement, targetInstanceName: string): boolean { + if (!ts.isCallExpression(node.expression)) { + return false; + } + const instanceName: string = findInstanceIdentifier(node.expression); + return instanceName === targetInstanceName; +} + +function findInstanceIdentifier(node: ts.CallExpression): string { + let instanceName: string = ''; + if (!ts.isPropertyAccessExpression(node.expression)) { + return instanceName; + } + const newNode: ts.PropertyAccessExpression = node.expression; + if (ts.isIdentifier(newNode.expression)) { + instanceName = newNode.expression.escapedText.toString(); + return instanceName; + } else if (ts.isCallExpression(newNode.expression)) { + instanceName = findInstanceIdentifier(newNode.expression); + } + return instanceName; +} + +function validateUIFunctionFormat(log: LogInfo[], block: ts.Statement): void { + log.push({ + message: `Only UI component syntax can be written here.`, + type: LogType.WARN, + pos: block.getStart() + }); +} + +export function parseStylesNode(node: ts.FunctionDeclaration | ts.MethodDeclaration, log: LogInfo[]): void { + const commonInstance: string = 'CommonInstance'; + if (node.body && ts.isBlock(node.body) && node.body.statements && node.body.statements.length) { + if (node.body.statements.length === 1 && ts.isExpressionStatement(node.body.statements[0])) { + !validateComponentInstance(node.body.statements[0], commonInstance) && + validateUIFunctionFormat(log, node.body.statements[0]); + return; + } + validateUIFunctionFormat(log, node.body.statements[0]); + } +} + function createEntryNode(node: ts.SourceFile, context: ts.TransformationContext, entryNodeKey: ts.Expression, id: number): ts.SourceFile { let cardRelativePath: string; diff --git a/compiler/src/userIntents_parser/intentType.ts b/compiler/src/userIntents_parser/intentType.ts index 9ac1f7fdf9ac0088ef700ed0779fd5fd378761ed..4babcbd76d86532a45b4eeed328ab18fd3a89cb2 100644 --- a/compiler/src/userIntents_parser/intentType.ts +++ b/compiler/src/userIntents_parser/intentType.ts @@ -227,13 +227,14 @@ intentEntryInfoChecker.paramValidators = { executeMode(v: ts.Expression): boolean { return v !== undefined && v !== null && ts.isArrayLiteralExpression(v) && v.elements.every(e => { + const enumValue: string = e?.getText().split('.').pop(); const validModes = [ - 'insightIntent.ExecuteMode.UI_ABILITY_FOREGROUND', - 'insightIntent.ExecuteMode.UI_ABILITY_BACKGROUND', - 'insightIntent.ExecuteMode.UI_EXTENSION_ABILITY', - 'insightIntent.ExecuteMode.SERVICE_EXTENSION_ABILITY' + 'UI_ABILITY_FOREGROUND', + 'UI_ABILITY_BACKGROUND', + 'UI_EXTENSION_ABILITY', + 'SERVICE_EXTENSION_ABILITY' ]; - return (ts.isNumericLiteral(e) && [0, 1, 2, 3].includes(Number(e.text))) || validModes.includes(e.getText()); + return (ts.isNumericLiteral(e) && [0, 1, 2, 3].includes(Number(e.text))) || validModes.includes(enumValue); }); }, intentName: validateRequiredString, diff --git a/compiler/src/userIntents_parser/parseUserIntents.ts b/compiler/src/userIntents_parser/parseUserIntents.ts index fa4bc79bd829742d6d0988a2f038e87174a47479..418b9804dd16ef162aeb259f2182ace4e4819817 100644 --- a/compiler/src/userIntents_parser/parseUserIntents.ts +++ b/compiler/src/userIntents_parser/parseUserIntents.ts @@ -34,7 +34,7 @@ import fs from 'fs'; import json5 from 'json5'; import { ProjectCollections } from 'arkguard'; import { - COMPONENT_USER_INTENTS_DECORATOR, + COMPONENT_USER_INTENTS_DECORATOR_LINK, COMPONENT_USER_INTENTS_DECORATOR_ENTITY, COMPONENT_USER_INTENTS_DECORATOR_ENTRY, COMPONENT_USER_INTENTS_DECORATOR_FUNCTION, @@ -44,7 +44,7 @@ import { } from '../pre_define'; import { CompileEvent, createAndStartEvent, stopEvent } from '../performance'; import {emitLogInfo, getTransformLog, LogInfo, LogType} from '../utils'; -import {ABILITY_SUBSYSTEM_CODE} from '../../lib/hvigor_error_code/hvigor_error_info'; +import {ABILITY_SUBSYSTEM_CODE} from '../hvigor_error_code/hvigor_error_info'; import {resetLog, transformLog} from '../process_ui_syntax'; type StaticValue = string | number | boolean | null | undefined | StaticValue[] | { [key: string]: StaticValue }; @@ -110,7 +110,7 @@ class ParseIntent { node: ts.ClassDeclaration, metaInfo: object, filePath: string, eventOrEventFactory: CompileEvent | undefined, transformLog: LogInfo[]): ts.Node { this.initInsightIntent(node, metaInfo, transformLog, filePath); const eventParseIntentTime: CompileEvent | undefined = createAndStartEvent(eventOrEventFactory, 'parseIntentTime'); - const definedDecorators: string[] = [COMPONENT_USER_INTENTS_DECORATOR, COMPONENT_USER_INTENTS_DECORATOR_ENTRY, + const definedDecorators: string[] = [COMPONENT_USER_INTENTS_DECORATOR_LINK, COMPONENT_USER_INTENTS_DECORATOR_ENTRY, COMPONENT_USER_INTENTS_DECORATOR_FUNCTION, COMPONENT_USER_INTENTS_DECORATOR_PAGE, COMPONENT_USER_INTENTS_DECORATOR_ENTITY, COMPONENT_USER_INTENTS_DECORATOR_FORM]; if (ts.isClassDeclaration(node) && !this.hasDecorator(node, [COMPONENT_USER_INTENTS_DECORATOR_FUNCTION])) { @@ -183,6 +183,16 @@ class ParseIntent { }); return; } + if (!projectConfig.pkgContextInfo) { + const errorMessage: string = 'Failed to generate standard OHMUrl.'; + this.transformLog.push({ + type: LogType.ERROR, message: errorMessage, pos: this.currentNode.getStart(), + code: '10111027', + description: 'InsightIntent Compiler Error', + solutions: ['Set useNormalizedOHMUrl to true in build-profile.json5'] + }); + return; + } const pkgParams: object = { pkgName: metaInfo.pkgName, pkgPath: metaInfo.pkgPath @@ -211,16 +221,6 @@ class ParseIntent { if (!isGlobalPathFlag) { return; } - if (!projectConfig.pkgContextInfo) { - const errorMessage: string = 'Failed to generate standard OHMUrl.'; - this.transformLog.push({ - type: LogType.ERROR, message: errorMessage, pos: this.currentNode.getStart(), - code: '10111027', - description: 'InsightIntent Compiler Error', - solutions: ['Set useNormalizedOHMUrl to true in build-profile.json5'] - }); - return; - } const Logger: IntentLogger = IntentLogger.getInstance(); const recordName: string = getNormalizedOhmUrlByFilepath(filepath, projectConfig, Logger, pkgParams, null); const intentObj: object = { @@ -228,7 +228,7 @@ class ParseIntent { 'decoratorClass': node.name.text }; const originalDecorator: string = '@' + decorator.expression.expression.getText(); - if (originalDecorator === COMPONENT_USER_INTENTS_DECORATOR) { + if (originalDecorator === COMPONENT_USER_INTENTS_DECORATOR_LINK) { this.handleLinkDecorator(intentObj, node, decorator); } else if (originalDecorator === COMPONENT_USER_INTENTS_DECORATOR_ENTRY) { this.handleEntryDecorator(intentObj, node, decorator, pkgParams); @@ -427,7 +427,7 @@ class ParseIntent { Object.assign(intentObj, { 'bundleName': projectConfig.bundleName, 'moduleName': projectConfig.moduleName, - 'decoratorType': COMPONENT_USER_INTENTS_DECORATOR + 'decoratorType': COMPONENT_USER_INTENTS_DECORATOR_LINK }); this.createObfuscation(node); if (this.isUpdateCompile) { @@ -572,14 +572,14 @@ class ParseIntent { if (!projectConfig.modulePathMap) { return; } - const jsonStr: string = fs.readFileSync(moduleJsonPath, 'utf8'); - const obj: object = json5.parse(jsonStr); - if (obj.module?.abilities) { - this.moduleJsonInfo.set('abilities', obj.module.abilities); - } - if (obj.module?.extensionAbilities) { - this.moduleJsonInfo.set('extensionAbilities', obj.module.extensionAbilities); - } + const jsonStr: string = fs.readFileSync(moduleJsonPath, 'utf8'); + const obj: object = json5.parse(jsonStr); + if (obj.module?.abilities) { + this.moduleJsonInfo.set('abilities', obj.module.abilities); + } + if (obj.module?.extensionAbilities) { + this.moduleJsonInfo.set('extensionAbilities', obj.module.extensionAbilities); + } } private validatePagePath(intentObj: object, pkgParams: object): void { @@ -882,7 +882,7 @@ class ParseIntent { const formBlackList: string[] = ['context']; if (decoratorType === COMPONENT_USER_INTENTS_DECORATOR_ENTRY && entryBlackList.includes(propName)) { return obj; - } else if (decoratorType === COMPONENT_USER_INTENTS_DECORATOR_ENTRY && formBlackList.includes(propName)) { + } else if (decoratorType === COMPONENT_USER_INTENTS_DECORATOR_FORM && formBlackList.includes(propName)) { return obj; } const tempschemaVerifyType: schemaVerifyType = { @@ -1675,7 +1675,7 @@ class ParseIntent { } } catch (e) { const errorMessage: string = `Failed to write to the intent configuration file.`; - this.transformLog.push({ + transformLog.errors.push({ type: LogType.ERROR, message: errorMessage, pos: this.currentNode.getStart(), code: '10110025', description: 'InsightIntent Compiler Error', @@ -1836,7 +1836,7 @@ class ParseIntent { writeJsonData.extractInsightIntents?.forEach(item => { if (duplicates.has(item.intentName)) { const errorMessage: string = `Duplicate intentName definitions found.`; - this.transformLog.push({ + transformLog.errors.push({ type: LogType.ERROR, message: errorMessage, pos: this.currentNode.getStart(), diff --git a/compiler/src/utils.ts b/compiler/src/utils.ts index 7b0a287a9963c60b92aedf56941748867058c237..ab86f0eea6b1ae2359c060504c824d541ae9428b 100644 --- a/compiler/src/utils.ts +++ b/compiler/src/utils.ts @@ -787,8 +787,12 @@ export class ProcessFileInfo { resourceTableChanged: boolean = false; currentArkTsFile: SpecialArkTSFileInfo; reUseProgram: boolean = false; - resourcesArr: Set = new Set(); - lastResourcesSet: Set = new Set(); + resourcesArr: Map = new Map(); // Rawfile and its hash + lastResourcesSet: Map = new Map(); + changedResourcesSet: Set = new Set(); // Rawfiles that are modified + resourcesForFiles: Map = new Map(); // Rawfiles used by each source file + lastResourcesForFiles: Map = new Map(); + hasResourcesCache: boolean = false; transformCacheFiles: { [fileName: string]: CacheFile } = {}; processBuilder: boolean = false; processGlobalBuilder: boolean = false; @@ -961,6 +965,10 @@ export class ProcessFileInfo { this.lastResourceList = new Set([...this.resourceList]); this.shouldInvalidFiles.clear(); this.resourcesArr.clear(); + this.lastResourcesSet.clear(); + this.changedResourcesSet.clear(); + this.resourcesForFiles.clear(); + this.lastResourcesForFiles.clear(); } setCurrentArkTsFile(): void { this.currentArkTsFile = new SpecialArkTSFileInfo(); @@ -1031,36 +1039,41 @@ export interface ExtendResult { componentName: string; } -export function resourcesRawfile(rawfilePath: string, resourcesArr: Set, resourceName: string = ''): void { +export function resourcesRawfile(rawfilePath: string, resourcesArr: Map, + getHashByFilePathFunc: Function | undefined, resourceName: string = ''): void { + if (!getHashByFilePathFunc) { + getHashByFilePathFunc = (filePath: string): string => '0'; + } if (fs.existsSync(process.env.rawFileResource) && fs.statSync(rawfilePath).isDirectory()) { const files: string[] = fs.readdirSync(rawfilePath); files.forEach((file: string) => { if (fs.statSync(path.join(rawfilePath, file)).isDirectory()) { - resourcesRawfile(path.join(rawfilePath, file), resourcesArr, resourceName ? resourceName + '/' + file : file); + resourcesRawfile(path.join(rawfilePath, file), resourcesArr, getHashByFilePathFunc, + resourceName ? resourceName + '/' + file : file); } else { if (resourceName) { - resourcesArr.add(resourceName + '/' + file); + resourcesArr.set(resourceName + '/' + file, getHashByFilePathFunc(rawfilePath + '/' + file)); } else { - resourcesArr.add(file); + resourcesArr.set(file, getHashByFilePathFunc(rawfilePath + '/' + file)); } } }); } } -export function differenceResourcesRawfile(oldRawfile: Set, newRawfile: Set): boolean { - if (oldRawfile.size !== 0 && oldRawfile.size === newRawfile.size) { - for (const singleRawfiles of oldRawfile.values()) { - if (!newRawfile.has(singleRawfiles)) { - return true; - } +export function differenceResourcesRawfile(oldRawfile: Map, newRawfile: Map, + changedRawFile: Set): boolean { + let res: boolean = oldRawfile.size !== newRawfile.size; + oldRawfile.forEach((hash, file) => { + if (!newRawfile.has(file)) { + changedRawFile.add(file); + res = true; + } else if (newRawfile.get(file) !== hash || newRawfile.get(file) === '0') { + changedRawFile.add(file); + res = true; } - return false; - } else if (oldRawfile.size === 0 && oldRawfile.size === newRawfile.size) { - return false; - } else { - return true; - } + }); + return res; } export function isString(text: unknown): text is string { @@ -1303,7 +1316,7 @@ export function removeDecorator(decorators: readonly ts.Decorator[], decoratorNa export function isFileInProject(filePath: string, projectRootPath: string): boolean { const relativeFilePath: string = toUnixPath(path.relative(toUnixPath(projectRootPath), toUnixPath(filePath))); // When processing ohmurl, hsp's filePath is consistent with moduleRequest - return fs.existsSync(filePath) && fs.statSync(filePath).isFile() && !relativeFilePath.startsWith('../'); + return path.isAbsolute(filePath) && fs.existsSync(filePath) && fs.statSync(filePath).isFile() && !relativeFilePath.startsWith('../'); } export function getProjectRootPath(filePath: string, projectConfig: Object, rootPathSet: Object): string { diff --git a/compiler/test/ark_compiler_ut/ets_checker.test.ts b/compiler/test/ark_compiler_ut/ets_checker.test.ts index 17d258b70485ffcc9f1e1c2eb791f18ad9280467..dd6d070a6b73673260aa672aa6953833a4c7d799 100644 --- a/compiler/test/ark_compiler_ut/ets_checker.test.ts +++ b/compiler/test/ark_compiler_ut/ets_checker.test.ts @@ -39,7 +39,8 @@ import { getMaxFlowDepth, MAX_FLOW_DEPTH_DEFAULT_VALUE, fileCache, - getFileContentWithHash + getFileContentWithHash, + areEqualArrays } from '../../lib/ets_checker'; import { TS_BUILD_INFO_SUFFIX } from '../../lib/pre_define'; import { @@ -60,17 +61,29 @@ mocha.describe('test ets_checker file api', function () { mocha.after(() => { delete this.rollup; const cacheFile: string = path.resolve(projectConfig.cachePath, '../.ts_checker_cache'); + const cacheFileWidget: string = path.resolve(projectConfig.cachePath, '../.ts_checker_cache_widget'); if (fs.existsSync(cacheFile)) { fs.unlinkSync(cacheFile); } + if (fs.existsSync(cacheFileWidget)) { + fs.unlinkSync(cacheFileWidget); + } const tsBuildInfoFilePath: string = path.resolve(projectConfig.cachePath, '..', TS_BUILD_INFO_SUFFIX); + const tsBuildInfoWidgetFilePath: string = path.resolve(projectConfig.cachePath, '..', TS_BUILD_INFO_SUFFIX + '_widget'); if (fs.existsSync(tsBuildInfoFilePath)) { fs.unlinkSync(tsBuildInfoFilePath); } + if (fs.existsSync(tsBuildInfoWidgetFilePath)) { + fs.unlinkSync(tsBuildInfoWidgetFilePath); + } const tsBuildInfoLinterFilePath: string = tsBuildInfoFilePath + '.linter'; + const tsBuildInfoWidgetLinterFilePath: string = tsBuildInfoWidgetFilePath + '.linter'; if (fs.existsSync(tsBuildInfoLinterFilePath)) { fs.unlinkSync(tsBuildInfoLinterFilePath); } + if (fs.existsSync(tsBuildInfoWidgetLinterFilePath)) { + fs.unlinkSync(tsBuildInfoWidgetLinterFilePath); + } }); mocha.it('1-1: test addLocalPackageSet for original ohmurl', function () { @@ -204,12 +217,97 @@ mocha.describe('test ets_checker file api', function () { }); }); +mocha.describe('test areEqualArrays function', () => { + // test cases for basic functions + mocha.it('1-1: should return true for identical arrays', () => { + expect(areEqualArrays(['a', 'b', 'c'], ['a', 'b', 'c'])).to.equal(true); + }); + + mocha.it('1-2: should return true for arrays with same elements in different order', () => { + expect(areEqualArrays(['a', 'b', 'c'], ['c', 'a', 'b'])).to.equal(true); + }); + + mocha.it('1-3: should return false for arrays with different elements', () => { + expect(areEqualArrays(['a', 'b', 'c'], ['a', 'b', 'd'])).to.equal(false); + }); + + mocha.it('1-4: should return false for arrays with different lengths', () => { + expect(areEqualArrays(['a', 'b', 'c'], ['a', 'b'])).to.equal(false); + expect(areEqualArrays(['a', 'b'], ['a', 'b', 'c'])).to.equal(false); + }); + + // test cases for boundary conditions + mocha.it('1-5: should return true for empty arrays', () => { + expect(areEqualArrays([], [])).to.equal(true); + }); + + mocha.it('1-6: should return false when one array is empty and other is not', () => { + expect(areEqualArrays([], ['a'])).to.equal(false); + expect(areEqualArrays(['a'], [])).to.equal(false); + }); + + // test cases for null and undefined scenarios + mocha.it('1-7: should return true when both arrays are undefined', () => { + expect(areEqualArrays(undefined, undefined)).to.equal(true); + }); + + mocha.it('1-8: should return true when both arrays are null', () => { + expect(areEqualArrays(null as any, null as any)).to.equal(true); + }); + + mocha.it('1-9: should return false when one array is undefined and other is not', () => { + expect(areEqualArrays(undefined, ['a'])).to.equal(false); + expect(areEqualArrays(['a'], undefined)).to.equal(false); + }); + + mocha.it('1-10: should return false when one array is null and other is not', () => { + expect(areEqualArrays(null as any, ['a'])).to.equal(false); + expect(areEqualArrays(['a'], null as any)).to.equal(false); + }); + + // test cases for duplicate elements + mocha.it('1-11: should handle duplicate elements correctly', () => { + expect(areEqualArrays(['a', 'a', 'b'], ['a', 'b', 'a'])).to.equal(true); + expect(areEqualArrays(['a', 'a', 'b'], ['a', 'b', 'b'])).to.equal(true); + }); + + // test case sensitivity scenarios + mocha.it('1-12: should be case sensitive', () => { + expect(areEqualArrays(['A', 'B'], ['a', 'b'])).to.equal(false); + expect(areEqualArrays(['A', 'B'], ['A', 'B'])).to.equal(true); + }); + + // test cases for special value scenarios + mocha.it('1-13: should handle special values', () => { + expect(areEqualArrays(['', 'null'], ['', 'null'])).to.equal(true); + expect(areEqualArrays(['0', '1'], ['0', '1'])).to.equal(true); + }); + + // test cases for performance + mocha.it('1-14: should handle large arrays efficiently', () => { + const largeArray1 = Array(10000).fill('item'); + const largeArray2 = Array(10000).fill('item'); + + expect(areEqualArrays(largeArray1, largeArray2)).to.equal(true); + }); + + // test cases for the edge condition + mocha.it('1-15: should return false for arrays with same length but different content', () => { + expect(areEqualArrays(['a', 'b'], ['a', 'c'])).to.equal(false); + }); + + mocha.it('1-16: should return true for single element arrays', () => { + expect(areEqualArrays(['a'], ['a'])).to.equal(true); + expect(areEqualArrays(['a'], ['b'])).to.equal(false); + }); + }); + mocha.describe('getMaxFlowDepth', () => { mocha.it('1-1: test should return the default value when maxFlowDepth is undefined', () => { const result = getMaxFlowDepth(); expect(result).to.equal(MAX_FLOW_DEPTH_DEFAULT_VALUE); }); - + mocha.it('1-2: test should return the default value and log a warning when maxFlowDepth is less than the minimum valid value', () => { const invalidMaxFlowDepth = 1999; projectConfig.projectArkOption = { @@ -219,7 +317,7 @@ mocha.describe('getMaxFlowDepth', () => { } const result = getMaxFlowDepth(); expect(result).to.equal(MAX_FLOW_DEPTH_DEFAULT_VALUE); - }); + }); mocha.it('1-3: test should return the value of maxFlowDepth when it is 2000 within the valid range', () => { const validMaxFlowDepth = 2000; @@ -231,7 +329,7 @@ mocha.describe('getMaxFlowDepth', () => { const result = getMaxFlowDepth(); expect(result).to.equal(validMaxFlowDepth); }); - + mocha.it('1-4: test should return the value of maxFlowDepth when it is 3000 within the valid range', () => { const validMaxFlowDepth = 3000; projectConfig.projectArkOption = { @@ -253,7 +351,7 @@ mocha.describe('getMaxFlowDepth', () => { const result = getMaxFlowDepth(); expect(result).to.equal(validMaxFlowDepth); }); - + mocha.it('1-6: test should return the default value and log a warning when maxFlowDepth is greater than the maximum valid value', () => { const invalidMaxFlowDepth = 65536; projectConfig.projectArkOption = { diff --git a/compiler/test/ark_compiler_ut/interop/interop_manager.test.ts b/compiler/test/ark_compiler_ut/interop/interop_manager.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f3569ad7629b1266c3ca160ea5c19c310048c83 --- /dev/null +++ b/compiler/test/ark_compiler_ut/interop/interop_manager.test.ts @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use rollupObject 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 { expect } from 'chai'; +import mocha from 'mocha'; +import path from "path"; + +import { + FileManager, + collectSDKInfo, + isBridgeCode, + getBrdigeCodeRootPath, + isMixCompile, + initConfigForInterop, + destroyInterop, + transformModuleNameToRelativePath + } from '../../../lib/fast_build/ark_compiler/interop/interop_manager'; +import { ARKTS_1_1, ARKTS_1_2, ARKTS_HYBRID } from '../../../lib/fast_build/ark_compiler/interop/pre_define'; +import { sdkConfigs } from '../../../main'; +import { toUnixPath } from '../../../lib/utils'; +import RollUpPluginMock from '../mock/rollup_mock/rollup_plugin_mock'; + +export interface ArkTSEvolutionModule { + language: string; + packageName: string; + moduleName: string; + modulePath: string; + declgenV1OutPath?: string; + declgenV2OutPath?: string; + declgenBridgeCodePath?: string; + declFilesPath?: string; + dynamicFiles: string[]; + staticFiles: string[]; + cachePath: string; + byteCodeHarInfo?: Object; +} + +mocha.describe('test interop_manager file api', function () { + mocha.before(function () { + const dependentModuleMap: Map = new Map(); + const dynamicSDKPath: Set = new Set([ + '/sdk/default/openharmony/ets/ets1.1/api', + '/sdk/default/openharmony/ets/ets1.1/arkts', + '/sdk/default/openharmony/ets/ets1.1/kits', + '/sdk/default/openharmony/ets/ets1.1/build-tools/ets-loader/declarations', + '/sdk/default/openharmony/ets/ets1.1/build-tools/ets-loader/component', + '/sdk/default/openharmony/ets/ets1.1/build-tools/components' + ]); + const staticSDKDeclPath: Set = new Set([ + '/sdk/default/openharmony/ets/ets1.2interop/declarations/kit', + '/sdk/default/openharmony/ets/ets1.2interop/declarations/api', + '/sdk/default/openharmony/ets/ets1.2interop/declarations/arkts' + ]); + const staticSDKGlueCodePath: Set = new Set([ + '/sdk/default/openharmony/ets/ets1.2interop/bridge/kit', + '/sdk/default/openharmony/ets/ets1.2interop/bridge/api', + '/sdk/default/openharmony/ets/ets1.2interop/bridge/arkts' + ]); + dependentModuleMap.set('application', { + language: ARKTS_1_1, + packageName: 'application', + moduleName: 'application', + modulePath: '/MyApplication16/application', + declgenV1OutPath: '/MyApplication16/application/build/default/intermediates/declgen/default/declgenV1', + declgenV2OutPath: '/MyApplication16/application/build/default/intermediates/declgen/default/declgenV2', + declgenBridgeCodePath: '/MyApplication16/application/build/default/intermediates/declgen/default/declgenBridgeCode', + declFilesPath: '/MyApplication16/application/build/default/intermediates/declgen/default/decl-fileInfo.json', + dynamicFiles: [], + staticFiles: [], + cachePath: '/MyApplication16/application/build/cache', + byteCodeHarInfo: {} + }); + + dependentModuleMap.set('harv2', { + language: ARKTS_1_2, + packageName: 'harv2', + moduleName: 'harv2', + modulePath: '/MyApplication16/harv2', + declgenV1OutPath: '/MyApplication16/harv2/build/default/intermediates/declgen/default/declgenV1', + declgenV2OutPath: '/MyApplication16/harv2/build/default/intermediates/declgen/default/declgenV2', + declgenBridgeCodePath: '/MyApplication16/harv2/build/default/intermediates/declgen/default/declgenBridgeCode', + declFilesPath: '/MyApplication16/harv2/build/default/intermediates/declgen/default/decl-fileInfo.json', + dynamicFiles: [], + staticFiles: [], + cachePath: '/MyApplication16/harv2/build/cache', + byteCodeHarInfo: {} + }); + + dependentModuleMap.set('dynamic1', { + language: ARKTS_1_1, + packageName: 'dynamic1', + moduleName: 'dynamic1', + modulePath: '/MyApplication16/dynamic1', + declgenV1OutPath: '/MyApplication16/dynamic1/build/default/intermediates/declgen/default/declgenV1', + declgenV2OutPath: '/MyApplication16/dynamic1/build/default/intermediates/declgen/default/declgenV2', + declgenBridgeCodePath: '/MyApplication16/dynamic1/build/default/intermediates/declgen/default/declgenBridgeCode', + declFilesPath: '/MyApplication16/dynamic1/build/default/intermediates/declgen/default/decl-fileInfo.json', + dynamicFiles: [], + staticFiles: [], + cachePath: '/MyApplication16/dynamic1/build/cache', + byteCodeHarInfo: {} + }); + + dependentModuleMap.set('hybrid', { + language: ARKTS_HYBRID, + packageName: 'hybrid', + moduleName: 'hybrid', + modulePath: '/MyApplication16/hybrid', + declgenV1OutPath: '/MyApplication16/hybrid/build/default/intermediates/declgen/default/declgenV1', + declgenV2OutPath: '/MyApplication16/hybrid/build/default/intermediates/declgen/default/declgenV2', + declgenBridgeCodePath: '/MyApplication16/hybrid/build/default/intermediates/declgen/default/declgenBridgeCode', + declFilesPath: '/MyApplication16/hybrid/build/default/intermediates/declgen/default/decl-fileInfo.json', + dynamicFiles: ['/MyApplication16/hybrid/fileV1.ets'], + staticFiles: ['/MyApplication16/hybrid/fileV2.ets'], + cachePath: '/MyApplication16/hybrid/build/cache', + byteCodeHarInfo: {} + }); + FileManager.cleanFileManagerObject(); + FileManager.initForTest( + dependentModuleMap, + undefined, + dynamicSDKPath, + staticSDKDeclPath, + staticSDKGlueCodePath); + }); + + mocha.after(() => { + FileManager.cleanFileManagerObject(); + }); + + mocha.it('1-1: test SDK path', function() { + const filePath = '/sdk/default/openharmony/ets/ets1.1/api/TestAPI.ets'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_1); + expect(result?.pkgName).to.equal('SDK'); + }); + + mocha.it('1-2: test ets-loader/declarations path', function() { + const filePath = '/sdk/default/openharmony/ets/ets1.1/build-tools/ets-loader/declarations/TestAPI.ets'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_1); + expect(result?.pkgName).to.equal('SDK'); + }); + + mocha.it('1-3: test ets-loader/component path', function() { + const filePath = '/sdk/default/openharmony/ets/ets1.1/build-tools/ets-loader/component/TestAPI.d.ts'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_1); + expect(result?.pkgName).to.equal('SDK'); + }); + + mocha.it('1-4: test SDK glue code path', function() { + const filePath = '/sdk/default/openharmony/ets/ets1.2interop/bridge/arkts/TestAPI.d.ts'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_2); + expect(result?.pkgName).to.equal('SDK'); + }); + + mocha.it('1-5: test SDK interop decl path', function() { + const filePath = '/sdk/default/openharmony/ets/ets1.2interop/declarations/kit/TestAPI.d.ts'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_2); + expect(result?.pkgName).to.equal('SDK'); + }); + + mocha.it('1-6: test source code from 1.1 module', function() { + const filePath = '/MyApplication16/application/sourceCode.ets'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_1); + expect(result?.pkgName).to.equal('application'); + }); + + mocha.it('1-7: test glue code file from 1.2 module', function() { + const filePath = '/MyApplication16/harv2/build/default/intermediates/declgen/default/declgenBridgeCode/sourceCode.ets'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_2); + expect(result?.pkgName).to.equal('harv2'); + }); + + mocha.it('1-8: test decl file from 1.2 module', function() { + const filePath = '/MyApplication16/harv2/build/default/intermediates/declgen/default/declgenV1/sourceCode.ets'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_2); + expect(result?.pkgName).to.equal('harv2'); + }); + + mocha.it('1-9: test source code file from hybrid module', function() { + const filePath = '/MyApplication16/hybrid/fileV1.ets'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_1); + expect(result?.pkgName).to.equal('hybrid'); + }); + + mocha.it('1-10: test source code file from hybrid module', function() { + const filePath = '/MyApplication16/hybrid/build/default/intermediates/declgen/default/declgenV1/file1'; + const result = FileManager.getInstance().getLanguageVersionByFilePath(filePath); + expect(result?.languageVersion).to.equal(ARKTS_1_2); + expect(result?.pkgName).to.equal('hybrid'); + }); + + mocha.it('2-1: test matchModulePath api with 1.1 module', function() { + const filePath = '/MyApplication16/dynamic1/sourceCode.ets'; + const moduleInfo = FileManager.matchModulePath(filePath); + expect(moduleInfo.languageVersion).to.equal(ARKTS_1_1); + expect(moduleInfo.pkgName).to.equal('dynamic1'); + }) + + mocha.it('2-2: test matchModulePath api with 1.2 module', function() { + const filePath = '/MyApplication16/harv2/sourceCode.ets'; + const moduleInfo = FileManager.matchModulePath(filePath); + expect(moduleInfo.languageVersion).to.equal(ARKTS_1_2); + expect(moduleInfo.pkgName).to.equal('harv2'); + }) + + mocha.it('2-3: test matchModulePath api with isHybrid module', function() { + const dymanicFilePath = '/MyApplication16/hybrid/fileV1.ets'; + const staticFilePath = '/MyApplication16/hybrid/fileV2.ets'; + + const moduleInfoV1 = FileManager.matchModulePath(dymanicFilePath); + expect(moduleInfoV1.languageVersion).to.equal(ARKTS_1_1); + expect(moduleInfoV1.pkgName).to.equal('hybrid'); + + const moduleInfoV2 = FileManager.matchModulePath(staticFilePath); + expect(moduleInfoV2.languageVersion).to.equal(ARKTS_1_2); + expect(moduleInfoV2.pkgName).to.equal('hybrid'); + }) + + mocha.it('3-1: test init SDK', function () { + const share = { + projectConfig: { + etsLoaderPath: '/mock/ets-loader', + } + }; + + const result = collectSDKInfo(share); + const expectedDynamicSDKPath = new Set([ + '/mock/ets-loader/declarations', + '/mock/ets-loader/components', + '/component', + '/mock/ets-loader' + ]); + sdkConfigs.forEach(({ apiPath }) => { + apiPath.forEach(path => { + expectedDynamicSDKPath.add(toUnixPath(path)); + }); + }); + const expectedStaticInteropDecl = new Set([ + '/ets1.2/build-tools/interop/declarations/kits', + '/ets1.2/build-tools/interop/declarations/api', + '/ets1.2/build-tools/interop/declarations/arkts' + ]); + + const expectedStaticGlueCode = new Set([ + '/ets1.2/build-tools/interop/bridge/kits', + '/ets1.2/build-tools/interop/bridge/api', + '/ets1.2/build-tools/interop/bridge/arkts' + ]); + expect([...result.dynamicSDKPath]).to.have.deep.members([...expectedDynamicSDKPath]); + expect([...result.staticSDKInteropDecl]).to.have.deep.members([...expectedStaticInteropDecl]); + expect([...result.staticSDKGlueCodePath]).to.have.deep.members([...expectedStaticGlueCode]); + }); +}); + +mocha.describe('isBridgeCode', function () { + const mockConfig = { + mixCompile: true, + dependentModuleMap: new Map([ + ['pkgA', { declgenBridgeCodePath: path.resolve('project/bridge/pkgA') }], + ['pkgB', { declgenBridgeCodePath: path.resolve('project/bridge/pkgB') }], + ]), + }; + + mocha.it('1-1: should return true when filePath is inside a declgenBridgeCodePath', function () { + const filePath = path.resolve('project/bridge/pkgA/utils/helper.ts'); + expect(isBridgeCode(filePath, mockConfig)).to.be.true; + }); + + mocha.it('1-2: should return false when filePath is outside all bridge code paths', function () { + const filePath = path.resolve('project/otherpkg/index.ts'); + expect(isBridgeCode(filePath, mockConfig)).to.be.false; + }); + + mocha.it('1-3: should return false when mixCompile is false', function () { + const config = { ...mockConfig, mixCompile: false }; + const filePath = path.resolve('project/bridge/pkgA/utils/helper.ts'); + expect(isBridgeCode(filePath, config)).to.be.false; + }); + + mocha.it('1-4: should return false when dependentModuleMap is empty', function () { + const config = { mixCompile: true, dependentModuleMap: new Map() }; + const filePath = path.resolve('project/bridge/pkgA/file.ts'); + expect(isBridgeCode(filePath, config)).to.be.false; + }); + + mocha.it('1-5: should return true for multiple matches, stop at first match', function () { + const config = { + mixCompile: true, + dependentModuleMap: new Map([ + ['pkg1', { declgenBridgeCodePath: path.resolve('path/one') }], + ['pkg2', { declgenBridgeCodePath: path.resolve('path/two') }], + ]), + }; + const filePath = path.resolve('path/one/module.ts'); + expect(isBridgeCode(filePath, config)).to.be.true; + }); +}); + +mocha.describe('test getBrdigeCodeRootPath api', function () { + mocha.it('1-1: should return bridgeCodePath from interopConfig when filePath matches moduleRootPath', function () { + const filePath = '/a/b/c/file.ts'; + const mockConfig: InteropConfig = { + mixCompile: false, + interopModuleInfo: new Map([ + ['/a/b', { + declgenBridgeCodePath: '/bridge/a/b', + declgenV1OutPath: '/v1' + }] + ]) + }; + + const result = getBrdigeCodeRootPath(filePath, mockConfig); + expect(result).to.equal('/bridge/a/b'); + }); + + mocha.it('1-2: should return undefined when filePath does not match any moduleRootPath', function () { + const filePath = '/x/y/z/file.ts'; + const mockConfig: InteropConfig = { + mixCompile: false, + interopModuleInfo: new Map([ + ['/a/b', { + declgenBridgeCodePath: '/bridge/a/b', + declgenV1OutPath: '/v1' + }] + ]) + }; + + const result = getBrdigeCodeRootPath(filePath, mockConfig); + expect(result).to.be.undefined; + }); + + mocha.it('1-3: should return process.env.entryBridgeCodePath when interopConfig is null', function () { + process.env.entryBridgeCodePath = '/default/bridge/path'; + const filePath = '/any/file.ts'; + + const result = getBrdigeCodeRootPath(filePath, undefined as any); + expect(result).to.equal('/default/bridge/path'); + }); +}); + +mocha.describe('test mixCompile', function () { + mocha.it('1-1 test mixCompile is false', function () { + expect(isMixCompile()).to.be.false; + }) + + mocha.it('1-2 test mixCompile is true', function () { + this.rollup = new RollUpPluginMock(); + this.rollup.build(); + this.rollup.share.projectConfig.mixCompile = true; + initConfigForInterop(this.rollup.share); + expect(isMixCompile()).to.be.true; + }) + + mocha.it('1-3 test mixCompile is false when destroy interop', function () { + this.rollup = new RollUpPluginMock(); + this.rollup.build(); + this.rollup.share.projectConfig.mixCompile = true; + initConfigForInterop(this.rollup.share); + destroyInterop() + expect(isMixCompile()).to.be.false; + }) +}) + +mocha.describe('test transformModuleNameToRelativePath api', function () { + mocha.before(function () { + this.rollup = new RollUpPluginMock(); + this.rollup.build(); + this.rollup.share.projectConfig.mixCompile = true; + initConfigForInterop(this.rollup.share); + }); + + mocha.it('1-1 test transformModuleNameToRelativePath when compile common module', function () { + this.rollup.share.projectConfig.isOhosTest = false; + + const moduleName = '/a/b/c/src/main/ets/module/file.ets'; + const result = transformModuleNameToRelativePath(moduleName); + + expect(result).to.equal('./ets/module/file.ets'); + }); + + mocha.it('1-2 test transformModuleNameToRelativePath when compile ohostest', function () { + this.rollup.share.projectConfig.isOhosTest = true; + initConfigForInterop(this.rollup.share); + const moduleName = '/a/b/c/src/ohosTest/ets/module/testfile.ets'; + const result = transformModuleNameToRelativePath(moduleName); + + expect(result).to.equal('./ets/module/testfile.ets'); + }); + + mocha.it('1-3 test transformModuleNameToRelativePath throws when sourceRoot not in path', function () { + this.rollup.share.projectConfig.isOhosTest = false; + + const invalidModuleName = '/a/b/c/invalidroot/ets/module/file.ets'; + + expect(() => transformModuleNameToRelativePath(invalidModuleName)).to.throw(Error); + }); + + mocha.after(() => { + delete this.rollup; + }); +}) diff --git a/compiler/test/ark_compiler_ut/interop/process_arkts_evolution.test.ts b/compiler/test/ark_compiler_ut/interop/process_arkts_evolution.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..6119d6bd35800320841a97d4e14d14a13dccafa9 --- /dev/null +++ b/compiler/test/ark_compiler_ut/interop/process_arkts_evolution.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use rollupObject 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 { expect } from 'chai'; +import mocha from 'mocha'; +import sinon from 'sinon'; + +import { + collectArkTSEvolutionModuleInfo, + addDeclFilesConfig, + pkgDeclFilesConfig, + arkTSModuleMap +} from '../../../lib/fast_build/ark_compiler/interop/process_arkts_evolution'; +import RollUpPluginMock from '../mock/rollup_mock/rollup_plugin_mock'; +import { + BUNDLE_NAME_DEFAULT, + HAR_DECLGENV2OUTPATH +} from '../mock/rollup_mock/common'; +import { CommonLogger } from '../../../lib/fast_build/ark_compiler/logger'; +import { ErrorCode } from '../../../lib/fast_build/ark_compiler/error_code'; + +mocha.describe('process arkts evolution tests', function () { + mocha.before(function () { + this.rollup = new RollUpPluginMock(); + }); + + mocha.after(() => { + delete this.rollup; + }); + + mocha.it('1-1: test error message of collectArkTSEvolutionModuleInfo (useNormalizedOHMUrl is false)', function () { + this.rollup.build(); + this.rollup.share.projectConfig.useNormalizedOHMUrl = false; + this.rollup.share.projectConfig.dependentModuleMap.set('evohar', { language: '1.2' }); + const throwArkTsCompilerErrorStub = sinon.stub(CommonLogger.getInstance(this.rollup), 'printErrorAndExit'); + try { + collectArkTSEvolutionModuleInfo(this.rollup.share); + } catch (e) { + } + expect(throwArkTsCompilerErrorStub.getCall(0).args[0].code === ErrorCode.ETS2BUNDLE_EXTERNAL_COLLECT_INTEROP_INFO_FAILED).to.be.true; + throwArkTsCompilerErrorStub.restore(); + }); + + mocha.it('1-2: test error message of collectArkTSEvolutionModuleInfo (1.2 module information is incorrect)', function () { + this.rollup.build(); + this.rollup.share.projectConfig.useNormalizedOHMUrl = true; + this.rollup.share.projectConfig.dependentModuleMap.set('evohar', { language: '1.2' }); + const throwArkTsCompilerErrorStub = sinon.stub(this.rollup.share, 'throwArkTsCompilerError'); + try { + collectArkTSEvolutionModuleInfo(this.rollup.share); + } catch(e) { + } + const errMsg: string = 'ArkTS:INTERNAL ERROR: Failed to collect arkTs evolution module info.\n' + + `Error Message: Failed to collect arkTs evolution module "evohar" info from rollup.`; + expect(throwArkTsCompilerErrorStub.getCall(0).args[1] === errMsg).to.be.true; + throwArkTsCompilerErrorStub.restore(); + }); + + mocha.it('1-3: test error message of collectArkTSEvolutionModuleInfo (1.1 module information is incorrect)', function () { + this.rollup.build(); + this.rollup.share.projectConfig.useNormalizedOHMUrl = true; + this.rollup.share.projectConfig.dependentModuleMap.set('har', { language: '1.1' }); + const throwArkTsCompilerErrorStub = sinon.stub(this.rollup.share, 'throwArkTsCompilerError'); + try { + collectArkTSEvolutionModuleInfo(this.rollup.share); + } catch(e) { + } + const errMsg: string = 'ArkTS:INTERNAL ERROR: Failed to collect arkTs evolution module info.\n' + + `Error Message: Failed to collect arkTs evolution module "har" info from rollup.`; + expect(throwArkTsCompilerErrorStub.getCall(0).args[1] === errMsg).to.be.true; + throwArkTsCompilerErrorStub.restore(); + }); + + mocha.it('2-1: test generate declFilesInfo in mixed compilation', function () { + pkgDeclFilesConfig['har'] = { + packageName: 'har', + files: {} + }; + const filePath = '/har/Index.ets' + const projectConfig = { + mainModuleName: 'entry', + bundleName: BUNDLE_NAME_DEFAULT, + pkgContextInfo: { + 'har': { + packageName: 'har', + version: '1.0.0', + isSO: false + } + } + } + arkTSModuleMap.set('har', { + language: '1.1', + pkgName: 'har', + declgenV2OutPath: HAR_DECLGENV2OUTPATH + }) + addDeclFilesConfig(filePath, projectConfig, undefined, '/har', 'har'); + const expectDeclPath: string = `${HAR_DECLGENV2OUTPATH}/Index.d.ets`; + const expectOhmUrl: string = `@normalized:N&entry&${BUNDLE_NAME_DEFAULT}&har/Index&1.0.0`; + expect(pkgDeclFilesConfig['har'].files.length !== 0).to.be.true; + expect(pkgDeclFilesConfig['har'].files['Index'].length !== 0).to.be.true; + expect(pkgDeclFilesConfig['har'].files['Index'].declPath === expectDeclPath).to.be.true; + expect(pkgDeclFilesConfig['har'].files['Index'].ohmUrl === expectOhmUrl).to.be.true; + arkTSModuleMap.clear(); + }); +}); \ No newline at end of file diff --git a/compiler/test/ark_compiler_ut/main.test.ts b/compiler/test/ark_compiler_ut/main.test.ts index 4c753981a37cb08a3b5cb5dae07996ad717205c3..2ebe19a918117683a00ff8f560d7984e4f3c5802 100644 --- a/compiler/test/ark_compiler_ut/main.test.ts +++ b/compiler/test/ark_compiler_ut/main.test.ts @@ -16,8 +16,9 @@ import mocha from 'mocha'; import fs from 'fs'; import { expect } from 'chai'; +import path from "path"; -import { setEntryArrayForObf, projectConfig } from '../../main'; +import { setEntryArrayForObf, projectConfig, setStartupPagesForObf } from '../../main'; mocha.describe('test main file api', function () { mocha.it('1-1: test setEntryArrayForObf', function () { @@ -36,4 +37,15 @@ mocha.describe('test main file api', function () { expect(projectConfig.entryArrayForObf[2]).to.equal('index'); expect(projectConfig.entryArrayForObf[3]).to.equal(''); }); + + mocha.it('1-2: test setStartupPagesForObf', function () { + projectConfig.aceModuleJsonPath = path.join(__dirname, '../../test/ark_compiler_ut/testdata/obfuscation/collectEntryFile/ark_module.json'); + projectConfig.aceProfilePath = path.join(__dirname, '../../test/ark_compiler_ut/testdata/obfuscation/collectEntryFile'); + projectConfig.entryArrayForObf = []; + setStartupPagesForObf(projectConfig); + expect(projectConfig.entryArrayForObf[0]).to.equal('pages/mainPage'); + expect(projectConfig.entryArrayForObf[1]).to.equal('pages/test0'); + expect(projectConfig.entryArrayForObf[2]).to.equal('pages/test1'); + expect(projectConfig.entryArrayForObf[3]).to.equal('pages/test2'); + }); }); \ No newline at end of file diff --git a/compiler/test/ark_compiler_ut/testdata/obfuscation/collectEntryFile/ark_module.json b/compiler/test/ark_compiler_ut/testdata/obfuscation/collectEntryFile/ark_module.json new file mode 100644 index 0000000000000000000000000000000000000000..d372cddb188087f868690274aa52d2ed43b32f9c --- /dev/null +++ b/compiler/test/ark_compiler_ut/testdata/obfuscation/collectEntryFile/ark_module.json @@ -0,0 +1,5 @@ +{ + "module": { + "appStartup": "$profile:startupConfig" + } +} \ No newline at end of file diff --git a/compiler/test/ark_compiler_ut/testdata/obfuscation/collectEntryFile/startupConfig.json b/compiler/test/ark_compiler_ut/testdata/obfuscation/collectEntryFile/startupConfig.json new file mode 100644 index 0000000000000000000000000000000000000000..6307c3aaf82b5e216fa8c65120e2996a0955ed98 --- /dev/null +++ b/compiler/test/ark_compiler_ut/testdata/obfuscation/collectEntryFile/startupConfig.json @@ -0,0 +1,14 @@ +{ + "startupTasks": [ + { + "srcEntry": "pages/test0" + }, + { + "srcEntry": "pages/test1" + }, + { + "srcEntry": "pages/test2" + } + ], + "configEntry": "pages/mainPage" +} \ No newline at end of file diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/ExtendInnerComponents.ets b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/ExtendInnerComponents.ets new file mode 100644 index 0000000000000000000000000000000000000000..381312e2543e19db2edc981c0322282802c70c86 --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/ExtendInnerComponents.ets @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022-2024 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. + */ +@Extend(Text) +function fancyText(weightValue: number, color: Color) { + .fontStyle(FontStyle.Italic) + .fontWeight(weightValue) + .backgroundColor(color) +} + +@Extend(testExtend) +function extendTestExtend () { + +} + +@Component +struct testExtend { + @State stringOne: string = 'stringOne'; + + build() { + Column() { + Text(this.stringOne) + } + } +} + +@Entry +@Component +struct ExtendInnerComponentsSummerpockets { + build(){ + } +} \ No newline at end of file diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/ExtendUISyntax.ets b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/ExtendUISyntax.ets new file mode 100644 index 0000000000000000000000000000000000000000..b9ffae0f853a687ca5c172dc99a08ce830a8f1b0 --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/ExtendUISyntax.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022-2024 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. + */ +@Extend(Text) +function fancyText(weightValue: number, color: Color) { + .fontStyle(FontStyle.Italic) + .fontWeight(weightValue) + .backgroundColor(color) +} + +@Extend(Button) +function extendButton() { + if (true) { + } else { + } +} + +@Extend(Button) +function extendButtonOne() { + switch (true){ + case true: + break; + } +} + +@Component +struct testExtend { + @State stringOne: string = 'stringOne'; + + build() { + Column() { + Text(this.stringOne) + } + } +} + +@Entry +@Component +struct ExtendInnerComponentsSummerpockets { + build(){ + } +} \ No newline at end of file diff --git a/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/StylesUISyntax.ets b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/StylesUISyntax.ets new file mode 100644 index 0000000000000000000000000000000000000000..8c18821527516e1a13b8f1f7b79a8873393206ee --- /dev/null +++ b/compiler/test/transform_ut/application/entry/src/main/ets/pages/utForValidate/Decorators/process_ui_syntax/StylesUISyntax.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022-2024 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. + */ +@Styles +function stylesOne() { + .width(100) + console.log(`this is a function`); +} + +@Styles +function stylesTwo() { + .height(200) +} + +@Entry +@ComponentV2 +struct testStylesV2 { + @Styles + innerStylesOne() { + .width(100) + console.log(`method`); + } + + build() { + Column() { + } + } +} + +@Component +struct testStyles { + @Styles + innerStyles(){ + if(true){ + .width(100) + } + } + + build() { + Row(){ + + } + } +} \ No newline at end of file diff --git a/compiler/test/transform_ut/helpers/pathConfig.ts b/compiler/test/transform_ut/helpers/pathConfig.ts index f981685eb677f2a2caa29c1ec8653eca002cc330..98a0152679d5d5694c24abbfbc1f0ce957ca661f 100644 --- a/compiler/test/transform_ut/helpers/pathConfig.ts +++ b/compiler/test/transform_ut/helpers/pathConfig.ts @@ -279,10 +279,13 @@ export const UT_VALIDATE_PAGES: string[] = [ 'Decorators/process_ui_syntax/EntryDecoParam', 'Decorators/process_ui_syntax/ExtendOneChild', + 'Decorators/process_ui_syntax/ExtendInnerComponents', + 'Decorators/process_ui_syntax/ExtendUISyntax', 'Decorators/process_ui_syntax/NoSrc', 'Decorators/process_ui_syntax/NotSupportResrcParam', 'Decorators/process_ui_syntax/NotSupportResrcType', 'Decorators/process_ui_syntax/StylesNoParam', + 'Decorators/process_ui_syntax/StylesUISyntax', 'Decorators/process_ui_syntax/UnknownSrc', 'Decorators/process_ui_syntax/UnknownSrcName', 'Decorators/process_ui_syntax/UnknownSrcType', diff --git a/compiler/test/transform_ut_error.json b/compiler/test/transform_ut_error.json index a3d04ea0a9bd57204ee8ad3951f79e9e8ca3d787..91dec88b07c07e0b8a0bed12de5d97957d8a63e5 100644 --- a/compiler/test/transform_ut_error.json +++ b/compiler/test/transform_ut_error.json @@ -316,6 +316,20 @@ "type": "ERROR", "code": "10905108" }, + "ExtendInnerComponents": { + "message": "'testExtend' parameter cannot be passed in the '@Extend' decorator.", + "type": "WARN" + }, + "ExtendUISyntax": [ + { + "message": "Only UI component syntax can be written here.", + "type": "WARN" + }, + { + "message": "Only UI component syntax can be written here.", + "type": "WARN" + } + ], "UnknownSrc": { "message": "Unknown resource source 'hap'.", "type": "ERROR", @@ -350,6 +364,20 @@ "type": "ERROR", "code": "10905105" }, + "StylesUISyntax": [ + { + "message": "Only UI component syntax can be written here.", + "type": "WARN" + }, + { + "message": "Only UI component syntax can be written here.", + "type": "WARN" + }, + { + "message": "Only UI component syntax can be written here.", + "type": "WARN" + } + ], "ExceededPreview": { "message": "A page can contain at most 10 '@Preview' decorators.", "type": "ERROR", diff --git a/koala-wrapper/koalaui/interop/src/cpp/interop-logging.cc b/koala-wrapper/koalaui/interop/src/cpp/interop-logging.cc index 60f5eb3e9c24f4a13e6c3f5235dd9b4af3a931f2..349e420ee3856a894b97da25e9d5bee410ed97f8 100644 --- a/koala-wrapper/koalaui/interop/src/cpp/interop-logging.cc +++ b/koala-wrapper/koalaui/interop/src/cpp/interop-logging.cc @@ -35,7 +35,9 @@ void startGroupedLog(int index) { if (index >= static_cast(groupedLogs.size())) { groupedLogs.resize(index + 1); for (int i = 0; i <= index; i++) { - if (!groupedLogs[i]) groupedLogs[i] = new Log(); + if (!groupedLogs[i]) { + groupedLogs[i] = new Log(); + } } } groupedLogs[index]->isActive = true; @@ -55,10 +57,6 @@ void appendGroupedLog(int index, const char* str) { } const char* getGroupedLog(int index) { - if (index < static_cast(groupedLogs.size())) { - const std::string& log = groupedLogs[index]->log; - return log.c_str(); - } return ""; } diff --git a/koala-wrapper/native/BUILD.gn b/koala-wrapper/native/BUILD.gn index a79c063ecef2336c3ea7d9e7250e8c875e227050..fdef3d53a36587499b1ac1696312904c2d5b1816 100644 --- a/koala-wrapper/native/BUILD.gn +++ b/koala-wrapper/native/BUILD.gn @@ -15,8 +15,7 @@ import("//build/ohos.gni") shared_library("es2panda") { external_deps = [ - "ets_frontend:libes2panda_public_headers", - "node:node_header_notice", + "ets_frontend:libes2panda_public_headers" ] sources = [ "../koalaui/interop/src/cpp/common-interop.cc", diff --git a/koala-wrapper/package.json b/koala-wrapper/package.json index 397203cec646de972938b59b55c0b39eef63b4d5..57f7692fad8271a35ccd0b743a4c8031d48ae53f 100644 --- a/koala-wrapper/package.json +++ b/koala-wrapper/package.json @@ -24,7 +24,8 @@ "@tsconfig/recommended": "1.0.8", "node-addon-api": "^8.3.0", "typescript": "^5.0.0", - "@types/node": "^18.0.0" + "@types/node": "^18.0.0", + "node-api-headers": "0.0.5" }, "imports": { "#koalaui/interop": { diff --git a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts index 1f66f9148fcfbc97c09816a867f1c4ab84f38fef..f8307a69bacc380cb5b8d7ddb30bcc415501bc39 100644 --- a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts +++ b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { updateNodeByNode } from "../utilities/private" +import { updateNodeByNode } from '../utilities/private'; import { ArrowFunctionExpression, AssignmentExpression, @@ -29,10 +29,10 @@ import { StructDeclaration, VariableDeclaration, VariableDeclarator, - ETSStringLiteralType -} from "../types" -import { MemberExpression } from "../to-be-generated/MemberExpression" -import { AstNode } from "../peers/AstNode" + ETSStringLiteralType, +} from '../types'; +import { MemberExpression } from '../to-be-generated/MemberExpression'; +import { AstNode } from '../peers/AstNode'; import { AnnotationUsage, BinaryExpression, @@ -79,66 +79,61 @@ import { ForUpdateStatement, ForInStatement, ForOfStatement, -} from "../../generated" -import { - Es2pandaModifierFlags -} from "../../generated/Es2pandaEnums" -import { - classPropertySetOptional, - hasModifierFlag -} from "../utilities/public" -import { updateIdentifier } from "../node-utilities/Identifier" -import { updateCallExpression } from "../node-utilities/CallExpression" -import { updateExpressionStatement } from "../node-utilities/ExpressionStatement" -import { updateMemberExpression } from "../node-utilities/MemberExpression" -import { updateFunctionDeclaration } from "../node-utilities/FunctionDeclaration" -import { updateBlockStatement } from "../node-utilities/BlockStatement" -import { updateArrowFunctionExpression } from "../node-utilities/ArrowFunctionExpression" -import { updateScriptFunction } from "../node-utilities/ScriptFunction" -import { updateStringLiteral } from "../node-utilities/StringLiteral" -import { updateNumberLiteral } from "../node-utilities/NumberLiteral" -import { updateETSParameterExpression } from "../node-utilities/ETSParameterExpression" -import { updateTSTypeParameter } from "../node-utilities/TSTypeParameter" -import { updateTSTypeParameterDeclaration } from "../node-utilities/TSTypeParameterDeclaration" -import { updateETSPrimitiveType } from "../node-utilities/ETSPrimitiveType" -import { updateETSTypeReference } from "../node-utilities/ETSTypeReference" -import { updateETSTypeReferencePart } from "../node-utilities/ETSTypeReferencePart" -import { updateETSImportDeclaration } from "../node-utilities/ETSImportDeclaration" -import { updateImportSpecifier } from "../node-utilities/ImportSpecifier" -import { updateVariableDeclaration } from "../node-utilities/VariableDeclaration" -import { updateVariableDeclarator } from "../node-utilities/VariableDeclarator" -import { updateETSUnionType } from "../node-utilities/ETSUnionType" -import { updateReturnStatement } from "../node-utilities/ReturnStatement" -import { updateIfStatement } from "../node-utilities/IfStatement" -import { updateBinaryExpression } from "../node-utilities/BinaryExpression" -import { updateClassDeclaration } from "../node-utilities/ClassDeclaration" -import { updateStructDeclaration } from "../node-utilities/StructDeclaration" -import { updateClassDefinition } from "../node-utilities/ClassDefinition" -import { updateClassProperty } from "../node-utilities/ClassProperty" -import { updateETSFunctionType } from "../node-utilities/ETSFunctionType" -import { updateFunctionExpression } from "../node-utilities/FunctionExpression" -import { updateMethodDefinition } from "../node-utilities/MethodDefinition" -import { updateSuperExpression } from "../node-utilities/SuperExpression" -import { updateTSTypeParameterInstantiation } from "../node-utilities/TSTypeParameterInstantiation" -import { updateTSInterfaceDeclaration } from "../node-utilities/TSInterfaceDeclaration" -import { updateTSInterfaceBody } from "../node-utilities/TSInterfaceBody" -import { updateUndefinedLiteral } from "../node-utilities/UndefinedLiteral" -import { updateAnnotationUsage, update1AnnotationUsage } from "../node-utilities/AnnotationUsage" -import { updateAssignmentExpression } from "../node-utilities/AssignmentExpression" -import { updateETSUndefinedType } from "../node-utilities/ETSUndefinedType" -import { updateConditionalExpression } from "../node-utilities/ConditionalExpression" -import { updateTSAsExpression } from "../node-utilities/TSAsExpression" -import { updateThisExpression } from "../node-utilities/ThisExpression" -import { updateTSTypeAliasDeclaration } from "../node-utilities/TSTypeAliasDeclaration" -import { updateTSNonNullExpression } from "../node-utilities/TSNonNullExpression" -import { updateChainExpression } from "../node-utilities/ChainExpression" -import { updateBlockExpression } from "../node-utilities/BlockExpression" -import { updateNullLiteral } from "../node-utilities/NullLiteral" -import { updateETSNewClassInstanceExpression } from "../node-utilities/ETSNewClassInstanceExpression" -import { updateObjectExpression } from "../node-utilities/ObjectExpression" -import { updateProperty } from "../node-utilities/Property" -import { updateTemplateLiteral } from "../node-utilities/TemplateLiteral" -import { updateArrayExpression } from "../node-utilities/ArrayExpression"; +} from '../../generated'; +import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; +import { classPropertySetOptional, hasModifierFlag } from '../utilities/public'; +import { updateIdentifier } from '../node-utilities/Identifier'; +import { updateCallExpression } from '../node-utilities/CallExpression'; +import { updateExpressionStatement } from '../node-utilities/ExpressionStatement'; +import { updateMemberExpression } from '../node-utilities/MemberExpression'; +import { updateFunctionDeclaration } from '../node-utilities/FunctionDeclaration'; +import { updateBlockStatement } from '../node-utilities/BlockStatement'; +import { updateArrowFunctionExpression } from '../node-utilities/ArrowFunctionExpression'; +import { updateScriptFunction } from '../node-utilities/ScriptFunction'; +import { updateStringLiteral } from '../node-utilities/StringLiteral'; +import { updateNumberLiteral } from '../node-utilities/NumberLiteral'; +import { updateETSParameterExpression } from '../node-utilities/ETSParameterExpression'; +import { updateTSTypeParameter } from '../node-utilities/TSTypeParameter'; +import { updateTSTypeParameterDeclaration } from '../node-utilities/TSTypeParameterDeclaration'; +import { updateETSPrimitiveType } from '../node-utilities/ETSPrimitiveType'; +import { updateETSTypeReference } from '../node-utilities/ETSTypeReference'; +import { updateETSTypeReferencePart } from '../node-utilities/ETSTypeReferencePart'; +import { updateETSImportDeclaration } from '../node-utilities/ETSImportDeclaration'; +import { updateImportSpecifier } from '../node-utilities/ImportSpecifier'; +import { updateVariableDeclaration } from '../node-utilities/VariableDeclaration'; +import { updateVariableDeclarator } from '../node-utilities/VariableDeclarator'; +import { updateETSUnionType } from '../node-utilities/ETSUnionType'; +import { updateReturnStatement } from '../node-utilities/ReturnStatement'; +import { updateIfStatement } from '../node-utilities/IfStatement'; +import { updateBinaryExpression } from '../node-utilities/BinaryExpression'; +import { updateClassDeclaration } from '../node-utilities/ClassDeclaration'; +import { updateStructDeclaration } from '../node-utilities/StructDeclaration'; +import { updateClassDefinition } from '../node-utilities/ClassDefinition'; +import { updateClassProperty } from '../node-utilities/ClassProperty'; +import { updateETSFunctionType } from '../node-utilities/ETSFunctionType'; +import { updateFunctionExpression } from '../node-utilities/FunctionExpression'; +import { updateMethodDefinition } from '../node-utilities/MethodDefinition'; +import { updateSuperExpression } from '../node-utilities/SuperExpression'; +import { updateTSTypeParameterInstantiation } from '../node-utilities/TSTypeParameterInstantiation'; +import { updateTSInterfaceDeclaration } from '../node-utilities/TSInterfaceDeclaration'; +import { updateTSInterfaceBody } from '../node-utilities/TSInterfaceBody'; +import { updateUndefinedLiteral } from '../node-utilities/UndefinedLiteral'; +import { updateAnnotationUsage, update1AnnotationUsage } from '../node-utilities/AnnotationUsage'; +import { updateAssignmentExpression } from '../node-utilities/AssignmentExpression'; +import { updateETSUndefinedType } from '../node-utilities/ETSUndefinedType'; +import { updateConditionalExpression } from '../node-utilities/ConditionalExpression'; +import { updateTSAsExpression } from '../node-utilities/TSAsExpression'; +import { updateThisExpression } from '../node-utilities/ThisExpression'; +import { updateTSTypeAliasDeclaration } from '../node-utilities/TSTypeAliasDeclaration'; +import { updateTSNonNullExpression } from '../node-utilities/TSNonNullExpression'; +import { updateChainExpression } from '../node-utilities/ChainExpression'; +import { updateBlockExpression } from '../node-utilities/BlockExpression'; +import { updateNullLiteral } from '../node-utilities/NullLiteral'; +import { updateETSNewClassInstanceExpression } from '../node-utilities/ETSNewClassInstanceExpression'; +import { updateObjectExpression } from '../node-utilities/ObjectExpression'; +import { updateProperty } from '../node-utilities/Property'; +import { updateTemplateLiteral } from '../node-utilities/TemplateLiteral'; +import { updateArrayExpression } from '../node-utilities/ArrayExpression'; import { updateTryStatement } from '../node-utilities/TryStatement'; import { updateForUpdateStatement } from '../node-utilities/ForUpdateStatement'; import { updateForInStatement } from '../node-utilities/ForInStatement'; diff --git a/koala-wrapper/src/arkts-api/index.ts b/koala-wrapper/src/arkts-api/index.ts index 936a9b0f83c0a44e57d33cd8e5b5c85a39a6b73d..5976735bf64295c7b28a266d4c0f8291dfac102c 100644 --- a/koala-wrapper/src/arkts-api/index.ts +++ b/koala-wrapper/src/arkts-api/index.ts @@ -13,71 +13,71 @@ * limitations under the License. */ -export * from "../Es2pandaEnums" -export * from "../generated/Es2pandaEnums" -export * from "../generated/peers/AnnotationUsage" -export * from "../generated/peers/BlockStatement" -export * from "../generated/peers/ETSPrimitiveType" -export * from "../generated/peers/TSInterfaceBody" -export * from "../generated/peers/TSInterfaceDeclaration" -export * from "../generated/peers/TSTypeParameterInstantiation" -export * from "../generated/peers/UndefinedLiteral" -export * from "../generated/peers/ETSUnionType" -export * from "../generated/peers/FunctionSignature" -export * from "../generated/peers/ETSFunctionType" -export * from "../generated/peers/StringLiteral" -export * from "../generated/peers/ThrowStatement" -export * from "../generated/peers/TypeNode" -export * from "../generated/peers/ClassDeclaration" -export * from "../generated/peers/ClassDefinition" -export * from "../generated/peers/Identifier" -export * from "../generated/peers/ETSTypeReference" -export * from "../generated/peers/ETSTypeReferencePart" -export * from "../generated/peers/ClassProperty" -export * from "../generated/peers/ReturnStatement" -export * from "../generated/peers/Expression" -export * from "../generated/peers/Statement" -export * from "../generated/peers/ImportSpecifier" -export * from "../generated/peers/TSAsExpression" -export * from "../generated/peers/ThisExpression" -export * from "../generated/peers/TSThisType" -export * from "../generated/peers/ETSImportDeclaration" -export * from "../generated/peers/ImportSource" -export * from "../generated/peers/ScriptFunction" -export * from "../generated/peers/TSTypeParameterDeclaration" -export * from "../generated/peers/TSTypeParameter" -export * from "../generated/peers/TSNonNullExpression" -export * from "../generated/peers/ChainExpression" -export * from "../generated/peers/ConditionalExpression" -export * from "../generated/peers/NullLiteral" -export * from "../generated/peers/TSTypeAliasDeclaration" -export * from "../generated/peers/ETSUndefinedType" -export * from "../generated/peers/ETSNewClassInstanceExpression" -export * from "../generated/peers/ObjectExpression" -export * from "../generated/peers/Property" -export * from "../generated/peers/BlockExpression" -export * from "../generated/peers/TSClassImplements" -export * from "../generated/peers/BooleanLiteral" -export * from "../generated/peers/TSArrayType" -export * from "../generated/peers/ArrayExpression"; -export * from "../generated/peers/ETSNullType"; +export * from '../Es2pandaEnums'; +export * from '../generated/Es2pandaEnums'; +export * from '../generated/peers/AnnotationUsage'; +export * from '../generated/peers/BlockStatement'; +export * from '../generated/peers/ETSPrimitiveType'; +export * from '../generated/peers/TSInterfaceBody'; +export * from '../generated/peers/TSInterfaceDeclaration'; +export * from '../generated/peers/TSTypeParameterInstantiation'; +export * from '../generated/peers/UndefinedLiteral'; +export * from '../generated/peers/ETSUnionType'; +export * from '../generated/peers/FunctionSignature'; +export * from '../generated/peers/ETSFunctionType'; +export * from '../generated/peers/StringLiteral'; +export * from '../generated/peers/ThrowStatement'; +export * from '../generated/peers/TypeNode'; +export * from '../generated/peers/ClassDeclaration'; +export * from '../generated/peers/ClassDefinition'; +export * from '../generated/peers/Identifier'; +export * from '../generated/peers/ETSTypeReference'; +export * from '../generated/peers/ETSTypeReferencePart'; +export * from '../generated/peers/ClassProperty'; +export * from '../generated/peers/ReturnStatement'; +export * from '../generated/peers/Expression'; +export * from '../generated/peers/Statement'; +export * from '../generated/peers/ImportSpecifier'; +export * from '../generated/peers/TSAsExpression'; +export * from '../generated/peers/ThisExpression'; +export * from '../generated/peers/TSThisType'; +export * from '../generated/peers/ETSImportDeclaration'; +export * from '../generated/peers/ImportSource'; +export * from '../generated/peers/ScriptFunction'; +export * from '../generated/peers/TSTypeParameterDeclaration'; +export * from '../generated/peers/TSTypeParameter'; +export * from '../generated/peers/TSNonNullExpression'; +export * from '../generated/peers/ChainExpression'; +export * from '../generated/peers/ConditionalExpression'; +export * from '../generated/peers/NullLiteral'; +export * from '../generated/peers/TSTypeAliasDeclaration'; +export * from '../generated/peers/ETSUndefinedType'; +export * from '../generated/peers/ETSNewClassInstanceExpression'; +export * from '../generated/peers/ObjectExpression'; +export * from '../generated/peers/Property'; +export * from '../generated/peers/BlockExpression'; +export * from '../generated/peers/TSClassImplements'; +export * from '../generated/peers/BooleanLiteral'; +export * from '../generated/peers/TSArrayType'; +export * from '../generated/peers/ArrayExpression'; +export * from '../generated/peers/ETSNullType'; -export * from "./types" -export * from "./utilities/private" -export * from "./utilities/public" -export * from "./utilities/performance" -export * from "./factory/nodeFactory" -export * from "./factory/nodeTests" -export * from "./visitor" -export * from "./peers/AstNode" -import "../generated" +export * from './types'; +export * from './utilities/private'; +export * from './utilities/public'; +export * from './utilities/performance'; +export * from './factory/nodeFactory'; +export * from './factory/nodeTests'; +export * from './visitor'; +export * from './peers/AstNode'; +import '../generated'; -export * from "./peers/Config" -export * from "./peers/Context" -export * from "./peers/Program" -export * from "./peers/ImportPathManager" -export * from "./peers/SourcePosition" -export * from "./peers/SourceRange" -export * from "./to-be-generated/MemberExpression" -export * from "./static/globalUtils" -export { global as arktsGlobal } from "./static/global"; +export * from './peers/Config'; +export * from './peers/Context'; +export * from './peers/Program'; +export * from './peers/ImportPathManager'; +export * from './peers/SourcePosition'; +export * from './peers/SourceRange'; +export * from './to-be-generated/MemberExpression'; +export * from './static/globalUtils'; +export { global as arktsGlobal } from './static/global'; diff --git a/koala-wrapper/src/generated/peers/ETSImportDeclaration.ts b/koala-wrapper/src/generated/peers/ETSImportDeclaration.ts index 919394d2ab7089a6335718a296ed630d625a7ed3..0752fe1dea06719c4cee4f741567675123ee6d1f 100644 --- a/koala-wrapper/src/generated/peers/ETSImportDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ETSImportDeclaration.ts @@ -26,45 +26,73 @@ import { KNativePointer, nodeByType, ArktsObject, - unpackString -} from "../../reexport-for-generated" + unpackString, +} from '../../reexport-for-generated'; +import { ImportDeclaration } from './ImportDeclaration'; +import { ImportSource } from './ImportSource'; +import { Es2pandaImportKinds } from './../Es2pandaEnums'; +import { StringLiteral } from './StringLiteral'; +import { Es2pandaImportFlags } from './../../Es2pandaEnums'; -import { ImportDeclaration } from "./ImportDeclaration" -import { ImportSource } from "./ImportSource" -import { Es2pandaImportKinds } from "./../Es2pandaEnums" -import { StringLiteral } from "./StringLiteral" -import { Es2pandaImportFlags } from "./../../Es2pandaEnums" export class ETSImportDeclaration extends ImportDeclaration { - constructor(pointer: KNativePointer) { - assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION) - super(pointer) - + constructor(pointer: KNativePointer) { + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION); + super(pointer); } - static createETSImportDeclaration(source: StringLiteral | undefined, specifiers: readonly AstNode[], importKind: Es2pandaImportKinds, program: ArktsObject, flags: Es2pandaImportFlags): ETSImportDeclaration { - return new ETSImportDeclaration(global.es2panda._CreateETSImportDeclaration(global.context, passNode(source), passNodeArray(specifiers), specifiers.length, importKind, passNode(program), flags)) + static createETSImportDeclaration( + source: StringLiteral | undefined, + specifiers: readonly AstNode[], + importKind: Es2pandaImportKinds, + program: ArktsObject, + flags: Es2pandaImportFlags + ): ETSImportDeclaration { + return new ETSImportDeclaration( + global.es2panda._CreateETSImportDeclaration( + global.context, + passNode(source), + passNodeArray(specifiers), + specifiers.length, + importKind, + passNode(program), + flags + ) + ); } - static updateETSImportDeclaration(original: ETSImportDeclaration | undefined, source: StringLiteral | undefined, specifiers: readonly AstNode[], importKind: Es2pandaImportKinds): ETSImportDeclaration { - return new ETSImportDeclaration(global.generatedEs2panda._UpdateETSImportDeclaration(global.context, passNode(original), passNode(source), passNodeArray(specifiers), specifiers.length, importKind)) + static updateETSImportDeclaration( + original: ETSImportDeclaration | undefined, + source: StringLiteral | undefined, + specifiers: readonly AstNode[], + importKind: Es2pandaImportKinds + ): ETSImportDeclaration { + return new ETSImportDeclaration( + global.generatedEs2panda._UpdateETSImportDeclaration( + global.context, + passNode(original), + passNode(source), + passNodeArray(specifiers), + specifiers.length, + importKind + ) + ); } get hasDecl(): boolean { - return global.generatedEs2panda._ETSImportDeclarationHasDeclConst(global.context, this.peer) + return global.generatedEs2panda._ETSImportDeclarationHasDeclConst(global.context, this.peer); } get isPureDynamic(): boolean { - return global.generatedEs2panda._ETSImportDeclarationIsPureDynamicConst(global.context, this.peer) + return global.generatedEs2panda._ETSImportDeclarationIsPureDynamicConst(global.context, this.peer); } get assemblerName(): string { - return unpackString(global.generatedEs2panda._ETSImportDeclarationAssemblerNameConst(global.context, this.peer)) + return unpackString( + global.generatedEs2panda._ETSImportDeclarationAssemblerNameConst(global.context, this.peer) + ); } - // get source(): StringLiteral | undefined { - // return unpackNode(global.generatedEs2panda._ETSImportDeclarationSourceConst(global.context, this.peer)) - // } get resolvedSource(): StringLiteral | undefined { - return unpackNode(global.generatedEs2panda._ETSImportDeclarationResolvedSourceConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ETSImportDeclarationResolvedSourceConst(global.context, this.peer)); } } export function isETSImportDeclaration(node: AstNode): node is ETSImportDeclaration { - return node instanceof ETSImportDeclaration + return node instanceof ETSImportDeclaration; } if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION)) { - nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION, ETSImportDeclaration) + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION, ETSImportDeclaration); } diff --git a/koala-wrapper/src/generated/peers/TryStatement.ts b/koala-wrapper/src/generated/peers/TryStatement.ts index 5c7c1a7562ab680d021f4c752fd424066c1fa0ad..72141cddca83e7d7380d4e79f40056574d98edd6 100644 --- a/koala-wrapper/src/generated/peers/TryStatement.ts +++ b/koala-wrapper/src/generated/peers/TryStatement.ts @@ -27,12 +27,12 @@ import { ArktsObject, unpackString, Es2pandaAstNodeType -} from "../../reexport-for-generated"; +} from '../../reexport-for-generated'; +import { BlockStatement } from './BlockStatement'; +import { CatchClause } from './CatchClause'; +import { LabelPair } from './LabelPair'; +import { Statement } from './Statement'; -import { BlockStatement } from "./BlockStatement"; -import { CatchClause } from "./CatchClause"; -import { LabelPair } from "./LabelPair"; -import { Statement } from "./Statement"; export class TryStatement extends Statement { constructor(pointer: KNativePointer) { assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TRY_STATEMENT)