* Copyright (c) 2026 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 * as path from 'path';
import * as fs from 'fs';
import { PluginContext } from '../../common/plugin-context';
import { INSIGHT_INTENT_FILE_NAME } from '../../common/predefines';
* InsightIntent 数据项基础接口
*/
export interface InsightIntentDataBase {
decoratorClass?: string | undefined;
className?: string | undefined;
decoratorFile: string | undefined;
decoratorType: string | undefined;
moduleName: string | undefined;
bundleName: string | undefined;
intentName?: string | undefined;
domain?: string | undefined;
intentVersion?: string | undefined;
displayName?: string | undefined;
displayDescription?: string | undefined;
schema?: string | undefined;
icon?: string | undefined;
llmDescription?: string | undefined;
keywords?: string[];
parameters?: Record<string, unknown> | null | undefined;
result?: Record<string, unknown> | null | undefined;
example?: string;
[key: string]: unknown;
}
* InsightIntentLink 数据接口
*/
export interface InsightIntentLinkData extends InsightIntentDataBase {
uri: string;
paramMappings?: Record<string, unknown> | null | undefined | string;
[key: string]: unknown;
}
* InsightIntentEntry 数据接口
*/
export interface InsightIntentEntryData extends InsightIntentDataBase {
abilityName: string;
executeMode: number[];
}
* InsightIntentPage 数据接口
*/
export interface InsightIntentPageData extends InsightIntentDataBase {
uiAbility?: string;
pagePath: string;
navigationId?: string;
navDestinationName?: string;
}
* InsightIntentFunction/Method 数据接口
*/
export interface InsightIntentFunctionData extends InsightIntentDataBase {
functionName?: string;
functionParamList?: string[];
}
* InsightIntentForm 数据接口
*/
export interface InsightIntentFormData extends InsightIntentDataBase {
formName: string;
abilityName?: string;
}
* InsightIntentEntity 数据接口
*/
export interface InsightIntentEntityData {
className: string;
decoratorFile: string;
decoratorType: string;
entityCategory: string;
entityId?: string;
parentClassName?: string;
parameters?: Record<string, unknown> | null | undefined;
supportedQueryProperties?: string[];
}
* 联合类型:所有 InsightIntent 数据类型
*/
export type InsightIntentData =
| InsightIntentLinkData
| InsightIntentEntryData
| InsightIntentPageData
| InsightIntentFunctionData
| InsightIntentFormData
| InsightIntentEntityData;
* InsightIntent 配置文件结构
*/
export interface InsightIntentConfig {
insightIntents: InsightIntentData[];
insightIntentsSrcEntry: Map<string, object[]>;
extractInsightIntents: InsightIntentData[];
}
interface InsightIntentCacheConfig {
extractInsightIntents: InsightIntentDataBase[];
entityIntents: InsightIntentEntityData[];
entityOwnerMapByFile: Record<string, Record<string, string[]>>;
entityExtendsMapByFile: Record<string, Record<string, string>>;
sourceFilePathMap: Record<string, string>;
}
* InsightIntentCollector 单例类
* 用于收集编译过程中的 InsightIntentLink 装饰器信息
*/
export class InsightIntentCollector {
private static instance: InsightIntentCollector;
private intents: Map<string, InsightIntentData> = new Map();
private fileIntentKeys: Map<string, Set<string>> = new Map();
private touchedFiles: Set<string> = new Set();
private allCompiledSourceFiles: Map<string, string> = new Map();
private entityOwnerMapByFile: Map<string, Map<string, Set<string>>> = new Map();
private entityExtendsMapByFile: Map<string, Map<string, string>> = new Map();
private constructor() {}
public static getInstance(): InsightIntentCollector {
if (!InsightIntentCollector.instance) {
InsightIntentCollector.instance = new InsightIntentCollector();
}
return InsightIntentCollector.instance;
}
* 添加一个 InsightIntent 数据项
*/
public addIntent(intent: InsightIntentData): void {
const identifier = 'decoratorClass' in intent ? intent.decoratorClass :
'className' in intent ? intent.className : 'Unknown';
const decoratorType = 'decoratorType' in intent ? intent.decoratorType : 'UnknownDecorator';
const intentIdentity = 'intentName' in intent && intent.intentName ? intent.intentName :
'functionName' in intent && intent.functionName ? intent.functionName :
'entityCategory' in intent && intent.entityCategory ? intent.entityCategory :
'formName' in intent && intent.formName ? intent.formName :
'default';
const key = `${intent.decoratorFile}#${identifier}#${decoratorType}#${intentIdentity}`;
this.intents.set(key, intent);
if (intent.decoratorFile) {
if (!this.touchedFiles.has(intent.decoratorFile)) {
this.touchedFiles.add(intent.decoratorFile);
this.removeFileData(intent.decoratorFile);
}
if (!this.fileIntentKeys.has(intent.decoratorFile)) {
this.fileIntentKeys.set(intent.decoratorFile, new Set());
}
this.fileIntentKeys.get(intent.decoratorFile)!.add(key);
}
}
public markSourceFileTouched(decoratorFile: string | undefined): void {
if (!decoratorFile) {
return;
}
this.touchedFiles.add(decoratorFile);
this.removeFileData(decoratorFile);
}
private normalizeDecoratorFile(decoratorFile: string): string {
let normalizedPath = decoratorFile.replace(/\\/g, '/');
const srcMainIndex = normalizedPath.indexOf('/src/main/');
if (srcMainIndex !== -1) {
const relativePath = normalizedPath.substring(srcMainIndex + 10);
if (!relativePath.startsWith('./')) {
return './' + relativePath;
}
return relativePath;
}
return normalizedPath;
}
public recordCompiledFile(filePath: string, decoratorFile: string | undefined): void {
if (!decoratorFile || !filePath) {
return;
}
const normalizedDecoratorFile = this.normalizeDecoratorFile(decoratorFile);
this.allCompiledSourceFiles.set(normalizedDecoratorFile, filePath);
}
* 添加 entity 与 intent 的关联关系
* @param intentName intent 的名称
* @param entityClassName entity 类名
*/
public addEntityOwner(intentName: string, entityClassName: string, decoratorFile?: string): void {
if (!intentName || !entityClassName || !decoratorFile) {
return;
}
if (!this.entityOwnerMapByFile.has(decoratorFile)) {
this.entityOwnerMapByFile.set(decoratorFile, new Map());
}
const fileEntityOwners = this.entityOwnerMapByFile.get(decoratorFile)!;
if (!fileEntityOwners.has(intentName)) {
fileEntityOwners.set(intentName, new Set());
}
fileEntityOwners.get(intentName)!.add(entityClassName);
}
public addEntityInheritance(entityClassName: string, parentClassName: string, decoratorFile?: string): void {
if (!entityClassName || !parentClassName || !decoratorFile) {
return;
}
if (!this.entityExtendsMapByFile.has(decoratorFile)) {
this.entityExtendsMapByFile.set(decoratorFile, new Map());
}
this.entityExtendsMapByFile.get(decoratorFile)!.set(entityClassName, parentClassName);
}
* 获取所有收集的 InsightIntent 数据
*/
public getAllIntents(): InsightIntentData[] {
return Array.from(this.intents.values());
}
public getAllCompiledSourceFiles(): Map<string, string> {
return this.allCompiledSourceFiles;
}
* 清空收集的数据
*/
public clear(): void {
this.intents.clear();
this.fileIntentKeys.clear();
this.touchedFiles.clear();
this.allCompiledSourceFiles.clear();
this.entityOwnerMapByFile.clear();
this.entityExtendsMapByFile.clear();
}
private removeFileData(decoratorFile: string): void {
const existingKeys = this.fileIntentKeys.get(decoratorFile);
if (existingKeys) {
for (const key of existingKeys) {
this.intents.delete(key);
}
this.fileIntentKeys.delete(decoratorFile);
}
this.entityOwnerMapByFile.delete(decoratorFile);
this.entityExtendsMapByFile.delete(decoratorFile);
}
public getTouchedFiles(): string[] {
return Array.from(this.touchedFiles.values());
}
private buildEntityOwnerMap(
entityOwnerMapByFile: Map<string, Map<string, Set<string>>> = this.entityOwnerMapByFile
): Map<string, Set<string>> {
const entityOwnerMap: Map<string, Set<string>> = new Map();
for (const fileEntityOwners of entityOwnerMapByFile.values()) {
for (const [intentName, entityClassNames] of fileEntityOwners.entries()) {
if (!entityOwnerMap.has(intentName)) {
entityOwnerMap.set(intentName, new Set());
}
const targetEntityNames = entityOwnerMap.get(intentName)!;
for (const entityClassName of entityClassNames) {
targetEntityNames.add(entityClassName);
}
}
}
return entityOwnerMap;
}
private buildEntityExtendsMap(
entityExtendsMapByFile: Map<string, Map<string, string>> = this.entityExtendsMapByFile
): Map<string, string> {
const entityExtendsMap: Map<string, string> = new Map();
for (const fileEntityExtends of entityExtendsMapByFile.values()) {
for (const [entityClassName, parentClassName] of fileEntityExtends.entries()) {
entityExtendsMap.set(entityClassName, parentClassName);
}
}
return entityExtendsMap;
}
private expandEntityOwnersWithInheritance(entityOwnerMap: Map<string, Set<string>>, entityExtendsMap: Map<string, string>): void {
for (const [intentName, entityClassNames] of entityOwnerMap.entries()) {
const expandedClassNames = new Set<string>(entityClassNames);
for (const className of entityClassNames) {
this.visitEntityInheritance(className, expandedClassNames, entityExtendsMap);
}
entityOwnerMap.set(intentName, expandedClassNames);
}
}
private visitEntityInheritance(
entityClassName: string,
expandedClassNames: Set<string>,
entityExtendsMap: Map<string, string>
): void {
const parentClassName = entityExtendsMap.get(entityClassName);
if (!parentClassName || expandedClassNames.has(parentClassName)) {
return;
}
expandedClassNames.add(parentClassName);
this.visitEntityInheritance(parentClassName, expandedClassNames, entityExtendsMap);
}
* 将 entities 关联到对应的 intents
* 根据 entityOwnerMap 中的映射关系,将 entity 数据添加到对应的 intent 中
*/
private matchEntities(
regularIntents: InsightIntentDataBase[],
entities: InsightIntentEntityData[],
entityOwnerMapByFile: Map<string, Map<string, Set<string>>> = this.entityOwnerMapByFile,
entityExtendsMapByFile: Map<string, Map<string, string>> = this.entityExtendsMapByFile
): void {
const entityOwnerMap = this.buildEntityOwnerMap(entityOwnerMapByFile);
if (entities.length === 0 || entityOwnerMap.size === 0) {
return;
}
this.expandEntityOwnersWithInheritance(entityOwnerMap, this.buildEntityExtendsMap(entityExtendsMapByFile));
const entityMap: Map<string, InsightIntentEntityData> = new Map();
for (const entity of entities) {
entityMap.set(entity.className, entity);
}
const intentNameMap: Map<string, InsightIntentDataBase> = new Map();
for (const intent of regularIntents) {
if (intent.intentName) {
intentNameMap.set(intent.intentName, intent);
}
}
for (const [intentName, entityClassNames] of entityOwnerMap.entries()) {
const targetIntent = intentNameMap.get(intentName);
if (!targetIntent) {
continue;
}
const matchedEntities: InsightIntentEntityData[] = [];
for (const entityClassName of entityClassNames) {
const entity = entityMap.get(entityClassName);
if (entity) {
matchedEntities.push(entity);
}
}
if (matchedEntities.length > 0) {
targetIntent.entities = matchedEntities;
}
}
}
* 解析 $ref 引用,将实体的 parameters 内容内联到引用位置
* 遍历 intent 的 parameters 和 result,查找 $ref 引用,
* 并用匹配的 entity parameters 内容替换
*/
private resolveEntityRefs(regularIntents: InsightIntentDataBase[], entities: InsightIntentEntityData[]): void {
if (entities.length === 0) {
return;
}
const entitySchemaMap: Map<string, Record<string, unknown>> = new Map();
for (const entity of entities) {
const entityId = entity.parameters?.$id;
if (typeof entityId === 'string' && entity.parameters) {
entitySchemaMap.set(entityId, entity.parameters);
}
}
if (entitySchemaMap.size === 0) {
return;
}
for (const intent of regularIntents) {
if ((intent as InsightIntentDataBase).parameters) {
this.resolveRefsInObject((intent as InsightIntentDataBase).parameters, entitySchemaMap);
}
if ((intent as InsightIntentDataBase).result) {
this.resolveRefsInObject((intent as InsightIntentDataBase).result, entitySchemaMap);
}
}
}
* 递归解析对象中的 $ref 引用
* 当某个属性值对象包含 $ref 字段且其值与 entitySchemaMap 中的 $id 匹配时,
* 将该属性值替换为对应 entity 的 parameters 内容
*/
private resolveRefsInObject(obj: unknown, entitySchemaMap: Map<string, Record<string, unknown>>): void {
if (!obj || typeof obj !== 'object') {
return;
}
if (Array.isArray(obj)) {
for (const item of obj) {
this.resolveRefsInObject(item, entitySchemaMap);
}
return;
}
const objectRecord = obj as Record<string, unknown>;
for (const key of Object.keys(objectRecord)) {
const value = objectRecord[key];
if (value && typeof value === 'object' && !Array.isArray(value)) {
const valueRecord = value as Record<string, unknown>;
const refValue = valueRecord.$ref;
if (typeof refValue === 'string' && entitySchemaMap.has(refValue)) {
const entityParams = entitySchemaMap.get(refValue);
const resolvedContent = JSON.parse(JSON.stringify(entityParams));
objectRecord[key] = resolvedContent;
} else {
this.resolveRefsInObject(value, entitySchemaMap);
}
}
}
}
private cloneData<T>(value: T): T {
return JSON.parse(JSON.stringify(value)) as T;
}
private stripEntitiesFromRegularIntents(intents: InsightIntentDataBase[]): InsightIntentDataBase[] {
return intents.map((intent: InsightIntentDataBase) => {
const clonedIntent = this.cloneData(intent);
delete clonedIntent.entities;
return clonedIntent;
});
}
private serializeEntityOwnerMapByFile(
entityOwnerMapByFile: Map<string, Map<string, Set<string>>>
): Record<string, Record<string, string[]>> {
const result: Record<string, Record<string, string[]>> = {};
for (const [decoratorFile, intentMap] of entityOwnerMapByFile.entries()) {
result[decoratorFile] = {};
for (const [intentName, entityClassNames] of intentMap.entries()) {
result[decoratorFile][intentName] = Array.from(entityClassNames.values());
}
}
return result;
}
private deserializeEntityOwnerMapByFile(
rawData: unknown
): Map<string, Map<string, Set<string>>> {
const result: Map<string, Map<string, Set<string>>> = new Map();
if (!rawData || typeof rawData !== 'object') {
return result;
}
for (const [decoratorFile, intentMap] of Object.entries(rawData as Record<string, unknown>)) {
if (!intentMap || typeof intentMap !== 'object') {
continue;
}
const deserializedIntentMap: Map<string, Set<string>> = new Map();
for (const [intentName, entityClassNames] of Object.entries(intentMap as Record<string, unknown>)) {
if (!Array.isArray(entityClassNames)) {
continue;
}
deserializedIntentMap.set(intentName, new Set(entityClassNames.filter((item): item is string => typeof item === 'string')));
}
result.set(decoratorFile, deserializedIntentMap);
}
return result;
}
private serializeEntityExtendsMapByFile(
entityExtendsMapByFile: Map<string, Map<string, string>>
): Record<string, Record<string, string>> {
const result: Record<string, Record<string, string>> = {};
for (const [decoratorFile, extendsMap] of entityExtendsMapByFile.entries()) {
result[decoratorFile] = Object.fromEntries(extendsMap.entries());
}
return result;
}
private deserializeEntityExtendsMapByFile(
rawData: unknown
): Map<string, Map<string, string>> {
const result: Map<string, Map<string, string>> = new Map();
if (!rawData || typeof rawData !== 'object') {
return result;
}
for (const [decoratorFile, extendsMap] of Object.entries(rawData as Record<string, unknown>)) {
if (!extendsMap || typeof extendsMap !== 'object') {
continue;
}
const deserializedExtendsMap: Map<string, string> = new Map();
for (const [entityClassName, parentClassName] of Object.entries(extendsMap as Record<string, unknown>)) {
if (typeof parentClassName === 'string') {
deserializedExtendsMap.set(entityClassName, parentClassName);
}
}
result.set(decoratorFile, deserializedExtendsMap);
}
return result;
}
private readCacheFile(cacheFilePath: string | undefined): InsightIntentCacheConfig {
const emptyCache: InsightIntentCacheConfig = {
extractInsightIntents: [],
entityIntents: [],
entityOwnerMapByFile: {},
entityExtendsMapByFile: {},
sourceFilePathMap: {},
};
if (!cacheFilePath || !fs.existsSync(cacheFilePath)) {
return emptyCache;
}
try {
const rawContent = fs.readFileSync(cacheFilePath, 'utf-8');
const parsedContent = JSON.parse(rawContent) as Partial<InsightIntentCacheConfig>;
return {
extractInsightIntents: Array.isArray(parsedContent.extractInsightIntents) ? parsedContent.extractInsightIntents : [],
entityIntents: Array.isArray(parsedContent.entityIntents) ? parsedContent.entityIntents : [],
entityOwnerMapByFile: parsedContent.entityOwnerMapByFile ?? {},
entityExtendsMapByFile: parsedContent.entityExtendsMapByFile ?? {},
sourceFilePathMap: parsedContent.sourceFilePathMap ?? {},
};
} catch (error) {
console.warn('[InsightIntent] Failed to parse cache file:', error);
return emptyCache;
}
}
private detectDeletedFiles(
cachedSourceFilePathMap: Record<string, string>
): string[] {
const deletedFiles: string[] = [];
const cachedFiles = Object.keys(cachedSourceFilePathMap);
for (const decoratorFile of cachedFiles) {
if (this.touchedFiles.has(decoratorFile)) {
continue;
}
if (this.allCompiledSourceFiles.has(decoratorFile)) {
deletedFiles.push(decoratorFile);
continue;
}
const sourceFilePath = cachedSourceFilePathMap[decoratorFile];
if (sourceFilePath && !fs.existsSync(sourceFilePath)) {
deletedFiles.push(decoratorFile);
}
}
return deletedFiles;
}
private buildCacheFileContent(
regularIntents: InsightIntentDataBase[],
entityIntents: InsightIntentEntityData[],
entityOwnerMapByFile: Map<string, Map<string, Set<string>>>,
entityExtendsMapByFile: Map<string, Map<string, string>>,
sourceFilePathMap: Record<string, string>
): InsightIntentCacheConfig {
return {
extractInsightIntents: this.stripEntitiesFromRegularIntents(regularIntents),
entityIntents: this.cloneData(entityIntents),
entityOwnerMapByFile: this.serializeEntityOwnerMapByFile(entityOwnerMapByFile),
entityExtendsMapByFile: this.serializeEntityExtendsMapByFile(entityExtendsMapByFile),
sourceFilePathMap: sourceFilePathMap,
};
}
private mergeFileScopedSetMaps(
cachedMap: Map<string, Map<string, Set<string>>>,
currentMap: Map<string, Map<string, Set<string>>>,
touchedFileSet: Set<string>
): Map<string, Map<string, Set<string>>> {
const mergedMap: Map<string, Map<string, Set<string>>> = new Map();
for (const [decoratorFile, intentMap] of cachedMap.entries()) {
if (touchedFileSet.has(decoratorFile)) {
continue;
}
const clonedIntentMap: Map<string, Set<string>> = new Map();
for (const [intentName, entityClassNames] of intentMap.entries()) {
clonedIntentMap.set(intentName, new Set(entityClassNames));
}
mergedMap.set(decoratorFile, clonedIntentMap);
}
for (const [decoratorFile, intentMap] of currentMap.entries()) {
const clonedIntentMap: Map<string, Set<string>> = new Map();
for (const [intentName, entityClassNames] of intentMap.entries()) {
clonedIntentMap.set(intentName, new Set(entityClassNames));
}
mergedMap.set(decoratorFile, clonedIntentMap);
}
return mergedMap;
}
private mergeFileScopedStringMaps(
cachedMap: Map<string, Map<string, string>>,
currentMap: Map<string, Map<string, string>>,
touchedFileSet: Set<string>
): Map<string, Map<string, string>> {
const mergedMap: Map<string, Map<string, string>> = new Map();
for (const [decoratorFile, extendsMap] of cachedMap.entries()) {
if (touchedFileSet.has(decoratorFile)) {
continue;
}
mergedMap.set(decoratorFile, new Map(extendsMap));
}
for (const [decoratorFile, extendsMap] of currentMap.entries()) {
mergedMap.set(decoratorFile, new Map(extendsMap));
}
return mergedMap;
}
* 写入 JSON 配置文件到指定目录
* 只更新 extractInsightIntents,保留 insightIntents 和 insightIntentsSrcEntry
* Entity 按 1.1 旧链路格式挂载到对应 intent 的 entities 字段中,不单独落顶层字段
*/
public writeToFile(aceProfilePath: string, bundleName: string, cachePath?: string): boolean {
try {
const touchedFiles = this.getTouchedFiles();
const touchedFileSet = new Set(touchedFiles);
const intents = this.getAllIntents();
const cacheFilePath = cachePath ? path.join(cachePath, 'insight_compile_cache.json') : undefined;
const cacheConfig = this.readCacheFile(cacheFilePath);
const cachedEntityOwnerMapByFile = this.deserializeEntityOwnerMapByFile(cacheConfig.entityOwnerMapByFile);
const cachedEntityExtendsMapByFile = this.deserializeEntityExtendsMapByFile(cacheConfig.entityExtendsMapByFile);
const cachedSourceFilePathMap: Record<string, string> = cacheConfig.sourceFilePathMap || {};
const deletedFiles: string[] = this.detectDeletedFiles(cachedSourceFilePathMap);
const deletedFileSet = new Set(deletedFiles);
if (intents.length === 0 &&
cacheConfig.extractInsightIntents.length === 0 &&
cacheConfig.entityIntents.length === 0) {
return false;
}
if (!fs.existsSync(aceProfilePath)) {
fs.mkdirSync(aceProfilePath, { recursive: true });
}
if (cachePath && !fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath, { recursive: true });
}
const outputPath = path.join(aceProfilePath, INSIGHT_INTENT_FILE_NAME);
let existingConfig: Partial<InsightIntentConfig> = {};
if (fs.existsSync(outputPath)) {
try {
const existingContent = fs.readFileSync(outputPath, 'utf-8');
const parsed = JSON.parse(existingContent);
existingConfig = {
...(parsed.insightIntents !== undefined && { insightIntents: parsed.insightIntents }),
...(parsed.insightIntentsSrcEntry !== undefined && { insightIntentsSrcEntry: parsed.insightIntentsSrcEntry }),
};
} catch (error) {
console.warn('[InsightIntent] Failed to parse existing config:', error);
}
}
const regularIntents: InsightIntentDataBase[] = [];
const entities: InsightIntentEntityData[] = [];
for (const intent of intents) {
if ('bundleName' in intent) {
intent.bundleName = bundleName || intent.bundleName;
}
if (intent.decoratorType === '@InsightIntentEntity') {
entities.push(intent as InsightIntentEntityData);
} else {
regularIntents.push(intent as InsightIntentDataBase);
}
}
const preservedIntents = cacheConfig.extractInsightIntents.filter((intent: InsightIntentDataBase) => {
return !intent.decoratorFile ||
(!touchedFileSet.has(intent.decoratorFile) && !deletedFileSet.has(intent.decoratorFile));
});
const preservedEntities = cacheConfig.entityIntents.filter((entityIntent: InsightIntentEntityData) => {
return !entityIntent.decoratorFile ||
(!touchedFileSet.has(entityIntent.decoratorFile) && !deletedFileSet.has(entityIntent.decoratorFile));
});
const mergedRegularIntents = this.stripEntitiesFromRegularIntents([
...preservedIntents,
...regularIntents,
]);
const mergedEntities = this.cloneData([
...preservedEntities,
...entities,
]);
const mergedEntityOwnerMapByFile = this.mergeFileScopedSetMaps(
cachedEntityOwnerMapByFile,
this.entityOwnerMapByFile,
touchedFileSet
);
const mergedEntityExtendsMapByFile = this.mergeFileScopedStringMaps(
cachedEntityExtendsMapByFile,
this.entityExtendsMapByFile,
touchedFileSet
);
for (const [decoratorFile, filePath] of Object.entries(cachedSourceFilePathMap)) {
if (!this.allCompiledSourceFiles.has(decoratorFile)) {
this.allCompiledSourceFiles.set(decoratorFile, filePath);
}
}
const intentFiles = new Set<string>();
mergedRegularIntents.forEach(intent => {
if (intent.decoratorFile) {
intentFiles.add(intent.decoratorFile);
}
});
const sourceFilePathMap: Record<string, string> = {};
for (const [decoratorFile, filePath] of this.allCompiledSourceFiles.entries()) {
if (intentFiles.has(decoratorFile)) {
sourceFilePathMap[decoratorFile] = filePath;
}
}
this.matchEntities(mergedRegularIntents, mergedEntities, mergedEntityOwnerMapByFile, mergedEntityExtendsMapByFile);
this.resolveEntityRefs(mergedRegularIntents, mergedEntities);
mergedRegularIntents.sort((leftIntent, rightIntent) => {
return (leftIntent.decoratorFile || '').localeCompare(rightIntent.decoratorFile || '');
});
const config: Partial<InsightIntentConfig> = {
...existingConfig
};
config.extractInsightIntents = mergedRegularIntents;
fs.writeFileSync(outputPath, JSON.stringify(config, null, 2), 'utf-8');
if (cacheFilePath) {
const cacheFileContent = this.buildCacheFileContent(
mergedRegularIntents,
mergedEntities,
mergedEntityOwnerMapByFile,
mergedEntityExtendsMapByFile,
sourceFilePathMap
);
fs.writeFileSync(cacheFilePath, JSON.stringify(cacheFileContent, null, 2), 'utf-8');
}
return true;
} catch (error) {
console.error('[InsightIntent] Failed to write config file:', error);
return false;
}
}
* 从 PluginContext 中提取配置并尝试写入文件
* 这个方法封装了获取配置路径和调用 writeToFile 的逻辑
*
* @param context - 插件上下文对象
* @returns 写入是否成功
*/
public tryWriteFromContext(context: PluginContext): boolean {
try {
const projectConfig = context.getProjectConfig?.();
if (!projectConfig) {
return false;
}
if (!fs.existsSync(projectConfig.moduleRootPath)) {
fs.mkdirSync(projectConfig.moduleRootPath, { recursive: true });
}
const pathSegments = [
'build',
'default',
'intermediates',
'res',
'default',
'resources',
'base',
'profile'
];
if (!projectConfig || !projectConfig.aceProfilePath) {
if (projectConfig.moduleRootPath) {
projectConfig.aceProfilePath = path.join(projectConfig.moduleRootPath, ...pathSegments);
}else{
return false;
}
}
const aceProfilePath = projectConfig.aceProfilePath
if (!aceProfilePath) {
return false;
}
const bundleName = projectConfig.bundleName || '';
return this.writeToFile(aceProfilePath, bundleName, projectConfig.cachePath);
} catch (error) {
console.error('[InsightIntent] Failed to generate config:', error);
return false;
}
}
* 重置收集器(用于新的编译周期)
*/
public reset(): void {
this.clear();
}
}