From f6061b4cdc89f1a8a099742b2a926a20762ed8df Mon Sep 17 00:00:00 2001 From: Cuecuexiaoyu Date: Thu, 5 Jun 2025 17:08:44 +0800 Subject: [PATCH] develop resource Signed-off-by: Cuecuexiaoyu Change-Id: I7183b02dbb833359547f7e37799beeed5f8f5066 --- arkui-plugins/BUILD.gn | 19 +- arkui-plugins/common/log-collector.ts | 63 +++ arkui-plugins/common/plugin-context.ts | 10 + arkui-plugins/common/predefines.ts | 34 ++ .../ui-plugins/checked-transformer.ts | 13 +- .../ui-plugins/struct-translators/factory.ts | 177 ++++++-- .../struct-translators/struct-transformer.ts | 17 +- .../ui-plugins/struct-translators/utils.ts | 428 +++++++++++++++++- 8 files changed, 729 insertions(+), 32 deletions(-) create mode 100644 arkui-plugins/common/log-collector.ts diff --git a/arkui-plugins/BUILD.gn b/arkui-plugins/BUILD.gn index ef1316286..e7644ab32 100755 --- a/arkui-plugins/BUILD.gn +++ b/arkui-plugins/BUILD.gn @@ -33,8 +33,25 @@ action("gen_ui_plugins") { outputs = [ "$target_gen_dir" ] } +action("build_ets_sysResource") { + deps = [":gen_ui_plugins"] + script = "//developtools/ace_ets2bundle/generateSysResource.py" + ets_sysResource = "$target_gen_dir" + "/lib/ui-plugins/sysResource.js" + outputs = [ ets_sysResource ] + + _id_defined_json = "//base/global/system_resources/systemres/main/resources/base/element/id_defined.json" + inputs = [ _id_defined_json ] + + args = [ + "--input-json", + rebase_path(_id_defined_json, root_build_dir), + "--output-js", + rebase_path(ets_sysResource, root_build_dir), + ] +} + ohos_copy("ui_plugin") { - deps = [ ":gen_ui_plugins" ] + deps = [":gen_ui_plugins", ":build_ets_sysResource" ] sources = [ rebase_path("$target_gen_dir") ] outputs = [ target_out_dir + "/$target_name" ] module_source_dir = target_out_dir + "/$target_name" diff --git a/arkui-plugins/common/log-collector.ts b/arkui-plugins/common/log-collector.ts new file mode 100644 index 000000000..16e00bde2 --- /dev/null +++ b/arkui-plugins/common/log-collector.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { LogType } from './predefines'; + +interface LogInfo { + type: LogType; + message: string; + node: arkts.AstNode; + code: string; +} + +export function generateDiagnosticKind(logItem: LogInfo): arkts.DiagnosticKind { + return arkts.DiagnosticKind.create( + `${logItem.code}: ${logItem.message}`, + logItem.type === LogType.ERROR + ? arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_ERROR + : arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_WARNING + ); +} + +export class LogCollector { + public logInfos: LogInfo[]; + private static instance: LogCollector; + + private constructor() { + this.logInfos = []; + } + + static getInstance(): LogCollector { + if (!this.instance) { + this.instance = new LogCollector(); + } + return this.instance; + } + + reset(): void { + this.logInfos = []; + } + + collectLogInfo(logItem: LogInfo): void { + this.logInfos.push(logItem); + } + + emitLogInfo(): void { + this.logInfos.forEach((logItem: LogInfo) => { + arkts.Diagnostic.logDiagnostic(generateDiagnosticKind(logItem), arkts.getStartPosition(logItem.node)); + }); + } +} diff --git a/arkui-plugins/common/plugin-context.ts b/arkui-plugins/common/plugin-context.ts index 95843cb7b..2a07f0670 100644 --- a/arkui-plugins/common/plugin-context.ts +++ b/arkui-plugins/common/plugin-context.ts @@ -91,6 +91,16 @@ export interface ProjectConfig { moduleName: string; cachePath: string; dependentModuleList: DependentModuleConfig[]; + appResource: string; + rawFileResource: string; + buildLoaderJson: string; + hspResourcesMap: boolean; + compileHar: boolean; + byteCodeHar: boolean; + uiTransformOptimization: boolean; + resetBundleName: boolean; + allowEmptyBundleName: boolean; + moduleType: string; } export type PluginHandlerFunction = () => void; diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index 34a34eb6c..16065cfe8 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -35,6 +35,25 @@ export const CUSTOM_COMPONENT_IMPORT_SOURCE_NAME: string = 'arkui.component.cust export const ENTRY_POINT_IMPORT_SOURCE_NAME: string = 'arkui.UserView'; export const ARKUI_COMPONENT_COMMON_SOURCE_NAME: string = 'arkui.component.common'; +export enum ModuleType { + HAR = 'har', + ENTRY = 'entry', + FEATURE = 'feature', + SHARED = 'shared', +} + +export enum DefaultConfiguration { + HAR_DEFAULT_MODULE_NAME = '__harDefaultModuleName__', + HAR_DEFAULT_BUNDLE_NAME = '__harDefaultBundleName__', + DYNAMIC_MODULE_NAME = '__MODULE_NAME__', + DYNAMIC_BUNDLE_NAME = '__BUNDLE_NAME__', +} + +export enum LogType { + ERROR = 'ERROR', + WARN = 'WARN', +} + export enum Dollars { DOLLAR_RESOURCE = '$r', DOLLAR_RAWFILE = '$rawfile', @@ -127,6 +146,21 @@ export enum AnimationNames { ANIMATION_STOP = 'animationStop', } +export const RESOURCE_TYPE: Record = { + color: 10001, + float: 10002, + string: 10003, + plural: 10004, + boolean: 10005, + intarray: 10006, + integer: 10007, + pattern: 10008, + strarray: 10009, + media: 20000, + rawfile: 30000, + symbol: 40000, +}; + export const DECORATOR_TYPE_MAP = new Map([ [DecoratorNames.STATE, StateManagementTypes.STATE_DECORATED], [DecoratorNames.LINK, StateManagementTypes.LINK_SOURCE_TYPE], diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index 357a41830..745305fec 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -30,6 +30,10 @@ import { ScopeInfoCollection, findCanAddMemoFromArrowFunction, isResourceNode, + LoaderJson, + ResourceInfo, + loadBuildJson, + initResourceInfo } from './struct-translators/utils'; import { isBuilderLambda, isBuilderLambdaMethodDecl } from './builder-lambda-translators/utils'; import { isEntryWrapperClass } from './entry-translators/utils'; @@ -37,15 +41,20 @@ import { ImportCollector } from '../common/import-collector'; import { DeclarationCollector } from '../common/declaration-collector'; import { PropertyCache } from './property-translators/utils'; import { isArkUICompatible, generateArkUICompatible } from './interop/interop'; +import { LogCollector } from '../common/log-collector'; export class CheckedTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; projectConfig: ProjectConfig | undefined; + aceBuildJson: LoaderJson; + resourceInfo: ResourceInfo; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; this.scope = { customComponents: [] }; + this.aceBuildJson = loadBuildJson(this.projectConfig); + this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); } reset(): void { @@ -54,6 +63,7 @@ export class CheckedTransformer extends AbstractVisitor { PropertyCache.getInstance().reset(); ImportCollector.getInstance().reset(); DeclarationCollector.getInstance().reset(); + LogCollector.getInstance().reset(); } enter(node: arkts.AstNode): void { @@ -107,7 +117,7 @@ export class CheckedTransformer extends AbstractVisitor { } else if (arkts.isClassDeclaration(node)) { return structFactory.transformNormalClass(node); } else if (arkts.isCallExpression(node) && isResourceNode(node)) { - return structFactory.transformResource(node, this.projectConfig); + return structFactory.transformResource(node, this.projectConfig, this.resourceInfo); } else if (isArkUICompatible(node)) { return generateArkUICompatible(node as arkts.CallExpression); } else if (arkts.isTSInterfaceDeclaration(node)) { @@ -116,6 +126,7 @@ export class CheckedTransformer extends AbstractVisitor { return addMemoAnnotation(node); } else if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { ImportCollector.getInstance().insertCurrentImports(this.program); + LogCollector.getInstance().emitLogInfo(); } else if (arkts.isTSTypeAliasDeclaration(node)) { return structFactory.transformTSTypeAlias(node); } diff --git a/arkui-plugins/ui-plugins/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index 6e5ddff03..eae984fb7 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -36,7 +36,18 @@ import { InterfacePropertyTranslator, PropertyTranslator, } from '../property-translators'; -import { CustomComponentScopeInfo, isEtsGlobalClass } from './utils'; +import { + CustomComponentScopeInfo, + isEtsGlobalClass, + ResourceInfo, + checkRawfileResource, + generateResourceModuleName, + generateResourceBundleName, + isDynamicName, + preCheckResourceData, + ResourceParameter, + getResourceParams, +} from './utils'; import { collectStateManagementTypeImport, hasDecorator, PropertyCache } from '../property-translators/utils'; import { ProjectConfig } from '../../common/plugin-context'; import { DeclarationCollector } from '../../common/declaration-collector'; @@ -46,7 +57,9 @@ import { ARKUI_COMPONENT_COMMON_SOURCE_NAME, DecoratorNames, Dollars, + ModuleType, StateManagementTypes, + RESOURCE_TYPE, } from '../../common/predefines'; import { ObservedTrackTranslator } from '../property-translators/observedTrack'; @@ -61,25 +74,6 @@ export class factory { return member; } - /* - * generate _r() or _rawfile(). - */ - static generateTransformedResource( - resourceNode: arkts.CallExpression, - key: arkts.Identifier, - newArgs: arkts.AstNode[] - ): arkts.CallExpression { - const transformedKey: string = - key.name === Dollars.DOLLAR_RESOURCE ? Dollars.TRANSFORM_DOLLAR_RESOURCE : Dollars.TRANSFORM_DOLLAR_RAWFILE; - ImportCollector.getInstance().collectImport(transformedKey); - return arkts.factory.updateCallExpression( - resourceNode, - arkts.factory.createIdentifier(transformedKey), - resourceNode.typeArguments, - newArgs - ); - } - /* * create __initializeStruct method. */ @@ -457,17 +451,146 @@ export class factory { */ static transformResource( resourceNode: arkts.CallExpression, - projectConfig: ProjectConfig | undefined + projectConfig: ProjectConfig | undefined, + resourceInfo: ResourceInfo ): arkts.CallExpression { - if (!arkts.isIdentifier(resourceNode.expression)) { + if (!arkts.isIdentifier(resourceNode.expression) || !projectConfig) { return resourceNode; } - const newArgs: arkts.AstNode[] = [ - arkts.factory.create1StringLiteral(projectConfig?.bundleName ? projectConfig.bundleName : ''), - arkts.factory.create1StringLiteral(projectConfig?.moduleName ? projectConfig.moduleName : ''), - ...resourceNode.arguments, + const resourceKind: Dollars = resourceNode.expression.name as Dollars; + if (arkts.isStringLiteral(resourceNode.arguments[0])) { + return factory.processStringLiteralResourceNode( + resourceNode, + resourceInfo, + projectConfig, + resourceKind, + resourceNode.arguments[0] + ); + } else if (resourceNode.arguments && resourceNode.arguments.length) { + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams( + -1, + resourceKind === Dollars.DOLLAR_RAWFILE ? RESOURCE_TYPE.rawfile : -1, + Array.from(resourceNode.arguments) + ), + '', + false, + projectConfig, + resourceKind + ); + } + return resourceNode; + } + + /* + * Process string Literal type arguments for resource node. + */ + static processStringLiteralResourceNode( + resourceNode: arkts.CallExpression, + resourceInfo: ResourceInfo, + projectConfig: ProjectConfig, + resourceKind: Dollars, + literalArg: arkts.StringLiteral + ): arkts.CallExpression { + const resourceData: string[] = literalArg.str.trim().split('.'); + const fromOtherModule: boolean = !!resourceData.length && /^\[.*\]$/g.test(resourceData[0]); + if (resourceKind === Dollars.DOLLAR_RAWFILE) { + checkRawfileResource(resourceNode, literalArg, fromOtherModule, resourceInfo.rawfile); + let resourceId: number = projectConfig.moduleType === ModuleType.HAR ? -1 : 0; + let resourceModuleName: string = ''; + if (resourceData && resourceData[0] && fromOtherModule) { + resourceId = -1; + resourceModuleName = resourceData[0]; + } + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams(resourceId, RESOURCE_TYPE.rawfile, [literalArg]), + resourceModuleName, + fromOtherModule, + projectConfig, + Dollars.DOLLAR_RAWFILE + ); + } else { + return factory.processStringLiteralDollarResourceNode( + resourceNode, + resourceInfo, + projectConfig, + resourceData, + fromOtherModule + ); + } + } + + /* + * Process string Literal type arguments for $r node. + */ + static processStringLiteralDollarResourceNode( + resourceNode: arkts.CallExpression, + resourceInfo: ResourceInfo, + projectConfig: ProjectConfig, + resourceData: string[], + fromOtherModule: boolean + ): arkts.CallExpression { + if ( + preCheckResourceData(resourceNode, resourceData, resourceInfo.resourcesList, fromOtherModule, projectConfig) + ) { + const resourceId: number = + projectConfig.moduleType === ModuleType.HAR || + fromOtherModule || + !resourceInfo.resourcesList[resourceData[0]] + ? -1 + : resourceInfo.resourcesList[resourceData[0]].get(resourceData[1])![resourceData[2]]; + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams( + resourceId, + RESOURCE_TYPE[resourceData[1].trim()], + projectConfig.moduleType === ModuleType.HAR || fromOtherModule + ? Array.from(resourceNode.arguments) + : Array.from(resourceNode.arguments.slice(1)) + ), + resourceData.length ? resourceData[0] : '', + fromOtherModule, + projectConfig, + Dollars.DOLLAR_RESOURCE + ); + } + return resourceNode; + } + + /* + * generate tramsformed resource node, e.g. {id, type, params, bundleName, moduleName}. + */ + static generateTransformedResourceCall( + resourceNode: arkts.CallExpression, + resourceParams: ResourceParameter, + resourceModuleName: string, + fromOtherModule: boolean, + projectConfig: ProjectConfig, + resourceKind: Dollars + ): arkts.CallExpression { + const transformedKey: string = + resourceKind === Dollars.DOLLAR_RESOURCE + ? Dollars.TRANSFORM_DOLLAR_RESOURCE + : Dollars.TRANSFORM_DOLLAR_RAWFILE; + ImportCollector.getInstance().collectImport(transformedKey); + const isDynamicBundleOrModule: boolean = isDynamicName(projectConfig); + const args: arkts.AstNode[] = [ + arkts.factory.createNumericLiteral(resourceParams.id), + arkts.factory.createNumericLiteral(resourceParams.type), + arkts.factory.createStringLiteral(generateResourceBundleName(projectConfig, isDynamicBundleOrModule)), + arkts.factory.createStringLiteral( + generateResourceModuleName(projectConfig, isDynamicBundleOrModule, resourceModuleName, fromOtherModule) + ), + ...resourceParams.params, ]; - return this.generateTransformedResource(resourceNode, resourceNode.expression, newArgs); + return arkts.factory.updateCallExpression( + resourceNode, + arkts.factory.createIdentifier(transformedKey), + undefined, + args + ); } /** diff --git a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts index 05b470a09..1a9c88261 100644 --- a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts +++ b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts @@ -22,7 +22,16 @@ import { CustomComponentNames, isCustomComponentClass, } from '../utils'; -import { CustomComponentScopeInfo, findCanAddMemoFromArrowFunction, isResourceNode, ScopeInfoCollection } from './utils'; +import { + CustomComponentScopeInfo, + findCanAddMemoFromArrowFunction, + isResourceNode, + ScopeInfoCollection, + LoaderJson, + ResourceInfo, + loadBuildJson, + initResourceInfo, +} from './utils'; import { factory } from './factory'; import { isEntryWrapperClass } from '../entry-translators/utils'; import { factory as entryFactory } from '../entry-translators/factory'; @@ -33,11 +42,15 @@ import { PropertyCache } from '../property-translators/utils'; export class StructTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; projectConfig: ProjectConfig | undefined; + aceBuildJson: LoaderJson; + resourceInfo: ResourceInfo; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; this.scope = { customComponents: [] }; + this.aceBuildJson = loadBuildJson(this.projectConfig); + this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); } reset(): void { @@ -91,7 +104,7 @@ export class StructTransformer extends AbstractVisitor { } else if (arkts.isClassDeclaration(node)) { return factory.transformNormalClass(node); } else if (arkts.isCallExpression(node) && isResourceNode(node)) { - return factory.transformResource(node, this.projectConfig); + return factory.transformResource(node, this.projectConfig, this.resourceInfo); } else if (arkts.isTSInterfaceDeclaration(node)) { return factory.tranformInterfaceMembers(node, this.externalSourceName); } else if (findCanAddMemoFromArrowFunction(node)) { diff --git a/arkui-plugins/ui-plugins/struct-translators/utils.ts b/arkui-plugins/ui-plugins/struct-translators/utils.ts index c4687bb04..eba0ce6a4 100644 --- a/arkui-plugins/ui-plugins/struct-translators/utils.ts +++ b/arkui-plugins/ui-plugins/struct-translators/utils.ts @@ -13,12 +13,24 @@ * limitations under the License. */ +import * as fs from 'fs'; +import * as path from 'path'; import * as arkts from '@koalaui/libarkts'; import { CustomComponentInfo, isMemoAnnotation, MemoNames } from '../utils'; import { isDecoratorAnnotation } from '../property-translators/utils'; import { matchPrefix } from '../../common/arkts-utils'; -import { ARKUI_IMPORT_PREFIX_NAMES, DecoratorNames, Dollars } from '../../common/predefines'; +import { + ARKUI_IMPORT_PREFIX_NAMES, + DecoratorNames, + Dollars, + ModuleType, + DefaultConfiguration, + LogType, + RESOURCE_TYPE, +} from '../../common/predefines'; import { DeclarationCollector } from '../../common/declaration-collector'; +import { ProjectConfig } from '../../common/plugin-context'; +import { LogCollector } from '../../common/log-collector'; export type ScopeInfoCollection = { customComponents: CustomComponentScopeInfo[]; @@ -30,6 +42,31 @@ export type CustomComponentScopeInfo = CustomComponentInfo & { hasReusableRebind?: boolean; }; +type ResourceMap = Map>; + +export interface ResourceList { + [key: string]: ResourceMap; +} + +export interface ResourceInfo { + resourcesList: ResourceList; + rawfile: Set; +} + +export interface LoaderJson { + hspResourcesMap: Record; +} + +export interface ResourceParameter { + id: number; + type: number; + params: arkts.Expression[]; +} + +export function getResourceParams(id: number, type: number, params: arkts.Expression[]): ResourceParameter { + return { id, type, params }; +} + /** * Determine whether it is ETSGLOBAL class. * @@ -101,3 +138,392 @@ export function findCanAddMemoFromArrowFunction(node: arkts.AstNode): node is ar } return false; } + +/** + * Read the content of file 'loader.json'. + * + * @param projectConfig configuration information of the project. + */ +export function loadBuildJson(projectConfig: ProjectConfig | undefined): any { + if (!!projectConfig && projectConfig.buildLoaderJson && fs.existsSync(projectConfig.buildLoaderJson)) { + try { + const content = fs.readFileSync(projectConfig.buildLoaderJson, 'utf-8'); + const parsedContent = JSON.parse(content); + return parsedContent; + } catch (error) { + throw new Error('Error: The file is not a valid JSON format.'); + } + } + return {}; +} + +/** + * Initialize all resources information, including app resources, system resources, dependent hap resources and rawfile resources. + * + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. + */ +export function initResourceInfo(projectConfig: ProjectConfig | undefined, aceBuildJson: LoaderJson): ResourceInfo { + let resourcesList: ResourceList = { + app: new Map>(), + sys: new Map>(), + }; + let rawfile: Set = new Set(); + if (!!projectConfig) { + readAppResource(resourcesList, projectConfig, aceBuildJson, rawfile); + } + return { resourcesList, rawfile }; +} + +/** + * Fill in the resource details to the resourcesList and rawfile. + * + * @param resourcesList resources including app, sys and hsp. + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. + * @param rawfile rawfile resource name set. + */ +function readAppResource( + resourcesList: ResourceList, + projectConfig: ProjectConfig, + aceBuildJson: LoaderJson, + rawfile: Set +): void { + if ('hspResourcesMap' in aceBuildJson && aceBuildJson.hspResourcesMap) { + readHspResource(aceBuildJson, projectConfig, resourcesList); + } + readSystemResource(resourcesList); + if (!!projectConfig.appResource && fs.existsSync(projectConfig.appResource)) { + const appResource: string = fs.readFileSync(projectConfig.appResource, 'utf-8'); + const resourceArr: string[] = appResource.split(/\n/); + const resourceMap: ResourceMap = new Map>(); + processResourceArr(resourceArr, resourceMap, projectConfig.appResource); + for (let [key, value] of resourceMap) { + resourcesList.app.set(key, value); + } + } + if (projectConfig.rawFileResource) { + processResourcesRawfile(projectConfig, projectConfig.rawFileResource, rawfile); + } +} + +/** + * Fill in the resource details to the system resource. + * + * @param resourcesList resources including app, sys and hsp. + */ +function readSystemResource(resourcesList: ResourceList): void { + const sysResourcePath = path.resolve(__dirname, '../sysResource.js'); + if (fs.existsSync(sysResourcePath)) { + const sysObj: Record> = require(sysResourcePath).sys; + Object.keys(sysObj).forEach((key: string) => { + resourcesList.sys.set(key, sysObj[key]); + }); + } +} + +/** + * generate resource map. + * + * @param resourceArr lines of file 'ResourceTable.txt'. + * @param resourceMap A map that records the mapping of resource type, name and id. + * @param resourcePath path of file 'ResourceTable.txt'. + */ +function processResourceArr( + resourceArr: string[], + resourceMap: Map>, + resourcePath: string +): void { + for (let i = 0; i < resourceArr.length; i++) { + if (!resourceArr[i].length) { + continue; + } + const resourceData = resourceArr[i].split(/\s/); + if (resourceData.length === 3 && !isNaN(Number(resourceData[2]))) { + rescordResourceNameAndIdMap(resourceMap, resourceData); + } else { + console.warn(`ArkTS:WARN The format of file '${resourcePath}' is incorrect.`); + break; + } + } +} + +/** + * Construct the mapping of resource type, name and id with 'ResourceTable.txt'. + * + * @param resourceMap A map that records the mapping of resource type, name and id. + * @param resourceData array of type, name and id. + */ +function rescordResourceNameAndIdMap(resourceMap: Map>, resourceData: string[]): void { + if (resourceMap.get(resourceData[0])) { + const resourceNameAndId: Record = resourceMap.get(resourceData[0])!; + if (!resourceNameAndId[resourceData[1]] || resourceNameAndId[resourceData[1]] !== Number(resourceData[2])) { + resourceNameAndId[resourceData[1]] = Number(resourceData[2]); + } + } else { + let obj: Record = {}; + obj[resourceData[1]] = Number(resourceData[2]); + resourceMap.set(resourceData[0], obj); + } +} + +/** + * Fill in the resource details to the hsp resource. + * + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. + * @param resourcesList resources including app, sys and hsp. + */ +function readHspResource(aceBuildJson: LoaderJson, projectConfig: ProjectConfig, resourcesList: ResourceList): void { + projectConfig.hspResourcesMap = true; + for (const hspName in aceBuildJson.hspResourcesMap) { + if (fs.existsSync(aceBuildJson.hspResourcesMap[hspName])) { + const resourceMap: ResourceMap = new Map>(); + resourcesList[hspName] = new Map>(); + const hspResource: string = fs.readFileSync(aceBuildJson.hspResourcesMap[hspName], 'utf-8'); + const resourceArr: string[] = hspResource.split(/\n/); + processResourceArr(resourceArr, resourceMap, aceBuildJson.hspResourcesMap[hspName]); + for (const [key, value] of resourceMap) { + resourcesList[hspName].set(key, value); + } + } + } +} + +/** + * Record the information of the rawfile resource. + * + * @param projectConfig configuration information of the project. + * @param rawfilePath path of rawfile directory. + * @param rawfileSet a set includes rawfile resource names. + * @param resourceName combination of existing directory names. + */ +function processResourcesRawfile( + projectConfig: ProjectConfig, + rawfilePath: string, + rawfileSet: Set, + resourceName: string = '' +): void { + if (fs.existsSync(projectConfig.rawFileResource) && fs.statSync(rawfilePath).isDirectory()) { + const files: string[] = fs.readdirSync(rawfilePath); + files.forEach((file: string) => { + if (fs.statSync(path.join(rawfilePath, file)).isDirectory()) { + processResourcesRawfile( + projectConfig, + path.join(rawfilePath, file), + rawfileSet, + resourceName ? resourceName + '/' + file : file + ); + } else { + addRawfileResourceToSet(rawfileSet, file, resourceName); + } + }); + } +} + +/** + * Add rawfile name to the collection of rawfile set. + * + * @param rawfileSet a set includes rawfile resource names. + * @param file rawfile name. + * @param resourceName combination of existing directory names. + */ +function addRawfileResourceToSet(rawfileSet: Set, file: string, resourceName: string = ''): void { + if (resourceName) { + rawfileSet.add(resourceName + '/' + file); + } else { + rawfileSet.add(file); + } +} + +/** + * Verify whether the rawfile resource exists in the current module. + * + * @param resourceNode resource node. + * @param rawfileStr rawfile string. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param rawfileSet a set that records all the rawfile resources. + */ +export function checkRawfileResource( + resourceNode: arkts.CallExpression, + rawfileStr: arkts.StringLiteral, + fromOtherModule: boolean, + rawfileSet: Set +): void { + if (!fromOtherModule && !rawfileSet.has(rawfileStr.str)) { + LogCollector.getInstance().collectLogInfo({ + type: LogType.ERROR, + node: resourceNode, + message: `No such '${rawfileStr.str}' resource in current module.`, + code: '10904333', + }); + } +} + +/** + * Check the format and the existance of resource string literal. + * + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param literalArg string literal argument node. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param projectConfig configuration information of the project. + */ +export function preCheckResourceData( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + fromOtherModule: boolean, + projectConfig: ProjectConfig +): boolean { + let code: string | undefined; + let message: string | undefined; + if (resourceData.length !== 3) { + message = 'The input parameter is not supported.'; + code = '10905332'; + } + if (!RESOURCE_TYPE[resourceData[1]]) { + message = `The resource type ${resourceData[1]} is not supported.`; + code = '10906334'; + } + if (!!code && !!message) { + LogCollector.getInstance().collectLogInfo({ + type: LogType.ERROR, + node: resourceNode, + message: message, + code: code, + }); + return false; + } + return preCheckResourceDataExistance(resourceNode, resourceData, resourcesList, fromOtherModule, projectConfig); +} + +/** + * Check the existance of resource string literal when the format of the string literal is correct. + * + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param literalArg string literal argument node. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param projectConfig configuration information of the project. + */ +export function preCheckResourceDataExistance( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + fromOtherModule: boolean, + projectConfig: ProjectConfig +): boolean { + if (fromOtherModule) { + if (/^\[.*\]$/.test(resourceData[0]) && projectConfig.hspResourcesMap) { + const resourceDataFirst: string = resourceData[0].replace(/^\[/, '').replace(/\]$/, '').trim(); + return resourceCheck(resourceNode, resourceData, resourcesList, true, resourceDataFirst, false); + } else { + return resourceCheck(resourceNode, resourceData, resourcesList, false, resourceData[0], true); + } + } else { + return resourceCheck(resourceNode, resourceData, resourcesList, false, resourceData[0], false); + } +} + +/** + * Verify whether the app resource exists in the current module. + * + * @param resourceNode resource node. + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param isHarHspResourceModule flag about whether it is from hsp or har module. + * @param resourceDataFirst the first element of resource string literals. + * @param noHspResourcesMap the non-existence of hspResourcesMap. + */ +function resourceCheck( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + isHarHspResourceModule: boolean, + resourceDataFirst: string, + noHspResourcesMap: boolean +): boolean { + let checkResult: boolean = true; + const logType: LogType = isHarHspResourceModule ? LogType.WARN : LogType.ERROR; + let code: string | undefined; + let message: string | undefined; + if (!noHspResourcesMap && !resourcesList[resourceDataFirst]) { + code = '10903331'; + message = `Unknown resource source '${resourceDataFirst}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } else if (!noHspResourcesMap && !resourcesList[resourceDataFirst].get(resourceData[1])) { + code = '10903330'; + message = `Unknown resource type '${resourceData[1]}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } else if (!noHspResourcesMap && !resourcesList[resourceDataFirst].get(resourceData[1])![resourceData[2]]) { + code = '10903329'; + message = `Unknown resource name '${resourceData[2]}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } + if (!!code && !!message) { + LogCollector.getInstance().collectLogInfo({ + type: logType, + node: resourceNode, + message: message, + code: code, + }); + } + return checkResult; +} + +/** + * generate bundleName for $r and $rawfile. + * + * @param projectConfig project config. + * @param isDynamicBundleOrModule a flag for determining whether to use dynamic module name and bundle name. + */ +export function generateResourceBundleName(projectConfig: ProjectConfig, isDynamicBundleOrModule: boolean): string { + if (projectConfig.resetBundleName || projectConfig.allowEmptyBundleName) { + return ''; + } + if (isDynamicBundleOrModule) { + return DefaultConfiguration.DYNAMIC_BUNDLE_NAME; + } + return projectConfig.moduleType === ModuleType.HAR + ? DefaultConfiguration.HAR_DEFAULT_BUNDLE_NAME + : projectConfig.bundleName + ? projectConfig.bundleName + : ''; +} + +/** + * generate moduleName for $r and $rawfile. + * + * @param projectConfig project config. + * @param isDynamicBundleOrModule a flag for determining whether to use dynamic module name and bundle name. + */ +export function generateResourceModuleName( + projectConfig: ProjectConfig, + isDynamicBundleOrModule: boolean = false, + resourceModuleName: string, + fromOtherModule: boolean +): string { + if (fromOtherModule && resourceModuleName) { + return resourceModuleName.replace(/^\[|\]$/g, ''); + } + if (isDynamicBundleOrModule) { + return DefaultConfiguration.DYNAMIC_MODULE_NAME; + } + return projectConfig.moduleType === ModuleType.HAR + ? DefaultConfiguration.HAR_DEFAULT_MODULE_NAME + : projectConfig.moduleName + ? projectConfig.moduleName + : ''; +} + +/** + * Determine whether to use dynamic module name and bundle name. + * + * @param projectConfig project config. + */ +export function isDynamicName(projectConfig: ProjectConfig): boolean { + const isByteCodeHar: boolean = projectConfig.moduleType === ModuleType.HAR && projectConfig.byteCodeHar; + const uiTransformOptimization: boolean = !!projectConfig.uiTransformOptimization; + return uiTransformOptimization ? uiTransformOptimization : isByteCodeHar; +} -- Gitee