@lcap/nasl 4.4.0-beta.18 → 4.4.0-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/natural/exports/nasl2Files.d.ts.map +1 -1
- package/out/natural/exports/nasl2Files.js +98 -1
- package/out/natural/exports/nasl2Files.js.map +1 -1
- package/out/natural/exports/transform2BatchActions.d.ts +2 -0
- package/out/natural/exports/transform2BatchActions.d.ts.map +1 -1
- package/out/natural/exports/transform2BatchActions.js +740 -87
- package/out/natural/exports/transform2BatchActions.js.map +1 -1
- package/out/natural/parser/parseNaturalTS.d.ts.map +1 -1
- package/out/natural/parser/parseNaturalTS.js +81 -24
- package/out/natural/parser/parseNaturalTS.js.map +1 -1
- package/out/natural/parser/parseNaturalTSXView.d.ts.map +1 -1
- package/out/natural/parser/parseNaturalTSXView.js +29 -77
- package/out/natural/parser/parseNaturalTSXView.js.map +1 -1
- package/out/natural/transforms/checker/checkLcapEntity.d.ts +16 -0
- package/out/natural/transforms/checker/checkLcapEntity.d.ts.map +1 -0
- package/out/natural/transforms/checker/checkLcapEntity.js +334 -0
- package/out/natural/transforms/checker/checkLcapEntity.js.map +1 -0
- package/out/natural/transforms/checker/index.d.ts +1 -0
- package/out/natural/transforms/checker/index.d.ts.map +1 -1
- package/out/natural/transforms/checker/index.js +3 -1
- package/out/natural/transforms/checker/index.js.map +1 -1
- package/out/natural/transforms/transform2Entity.d.ts.map +1 -1
- package/out/natural/transforms/transform2Entity.js +3 -2
- package/out/natural/transforms/transform2Entity.js.map +1 -1
- package/out/natural/transforms/transform2GlobalLogicDeclaration.d.ts +1 -1
- package/out/natural/transforms/transform2GlobalLogicDeclaration.d.ts.map +1 -1
- package/out/natural/transforms/transform2GlobalLogicDeclaration.js +15 -1
- package/out/natural/transforms/transform2GlobalLogicDeclaration.js.map +1 -1
- package/out/natural/transforms/transform2Logic.d.ts +3 -3
- package/out/natural/transforms/transform2Logic.d.ts.map +1 -1
- package/out/natural/transforms/transform2Logic.js +34 -7
- package/out/natural/transforms/transform2Logic.js.map +1 -1
- package/out/natural/transforms/transform2LogicItem.d.ts +4 -0
- package/out/natural/transforms/transform2LogicItem.d.ts.map +1 -1
- package/out/natural/transforms/transform2LogicItem.js +11 -2
- package/out/natural/transforms/transform2LogicItem.js.map +1 -1
- package/out/natural/transforms/transform2Variable.d.ts +3 -3
- package/out/natural/transforms/transform2Variable.d.ts.map +1 -1
- package/out/natural/transforms/transform2Variable.js +32 -5
- package/out/natural/transforms/transform2Variable.js.map +1 -1
- package/out/natural/transforms/utils/parseDecorator.d.ts +27 -3
- package/out/natural/transforms/utils/parseDecorator.d.ts.map +1 -1
- package/out/natural/transforms/utils/parseDecorator.js +61 -6
- package/out/natural/transforms/utils/parseDecorator.js.map +1 -1
- package/out/natural/transforms/utils/transform2Directory.d.ts +59 -0
- package/out/natural/transforms/utils/transform2Directory.d.ts.map +1 -0
- package/out/natural/transforms/utils/transform2Directory.js +229 -0
- package/out/natural/transforms/utils/transform2Directory.js.map +1 -0
- package/out/natural/utils/index.d.ts +16 -0
- package/out/natural/utils/index.d.ts.map +1 -1
- package/out/natural/utils/index.js +26 -1
- package/out/natural/utils/index.js.map +1 -1
- package/out/natural/utils/nasl-file-types.d.ts +1 -1
- package/out/natural/utils/nasl-file-types.d.ts.map +1 -1
- package/out/natural/utils/nasl-file-types.js +7 -0
- package/out/natural/utils/nasl-file-types.js.map +1 -1
- package/out/server/naslServer.d.ts.map +1 -1
- package/out/server/naslServer.js +3 -3
- package/out/server/naslServer.js.map +1 -1
- package/package.json +9 -9
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* - enum: 枚举类型
|
|
18
18
|
* - logic: 业务逻辑
|
|
19
19
|
* - view: 视图组件
|
|
20
|
+
* - backendVariables: 后端变量
|
|
21
|
+
* - frontendVariables: 前端类型变量
|
|
20
22
|
*/
|
|
21
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
24
|
exports.applyBatchActions = exports.transform2BatchActions = exports.generateRouterContainerTemplate = void 0;
|
|
@@ -25,6 +27,12 @@ const concepts_1 = require("../../concepts");
|
|
|
25
27
|
const index_1 = require("../../index");
|
|
26
28
|
const utils_1 = require("../../utils");
|
|
27
29
|
const nasl_file_types_1 = require("../utils/nasl-file-types");
|
|
30
|
+
/**
|
|
31
|
+
* 检查项目类型是否允许空名称
|
|
32
|
+
*/
|
|
33
|
+
const isNameOptional = (itemType) => {
|
|
34
|
+
return ['backendVariables', 'frontendVariables', 'theme'].includes(itemType);
|
|
35
|
+
};
|
|
28
36
|
/**
|
|
29
37
|
* 生成空的路由容器模板
|
|
30
38
|
* 用于为嵌套视图创建父级容器视图
|
|
@@ -63,6 +71,14 @@ const getItemType = (namespace) => {
|
|
|
63
71
|
return 'logic';
|
|
64
72
|
if (namespace.includes('views'))
|
|
65
73
|
return 'view';
|
|
74
|
+
if (namespace.includes('extensions'))
|
|
75
|
+
return 'extensions';
|
|
76
|
+
if (namespace.includes('app.backend.variables'))
|
|
77
|
+
return 'backendVariables';
|
|
78
|
+
if (namespace.includes('theme.css'))
|
|
79
|
+
return 'theme';
|
|
80
|
+
if (/app\.frontendTypes\.[^.]+\.frontends\.[^.]+\.variables/.test(namespace))
|
|
81
|
+
return 'frontendVariables';
|
|
66
82
|
return 'unknown';
|
|
67
83
|
};
|
|
68
84
|
/**
|
|
@@ -151,65 +167,391 @@ const createViewFinder = (instance) => (name, parentViewNames) => {
|
|
|
151
167
|
// 顶级视图使用 findViewByName
|
|
152
168
|
return instance?.frontendTypes?.[0]?.frontends?.[0]?.findViewByName(name);
|
|
153
169
|
};
|
|
170
|
+
/**
|
|
171
|
+
* 类型守卫:判断是否为单节点处理器
|
|
172
|
+
*/
|
|
173
|
+
const isSingleNodeHandler = (handler) => {
|
|
174
|
+
return ['entity', 'structure', 'enum', 'logic'].includes(handler.type);
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* 类型守卫:判断是否为视图处理器
|
|
178
|
+
*/
|
|
179
|
+
const isViewHandler = (handler) => {
|
|
180
|
+
return handler.type === 'view';
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* 类型守卫:判断是否为全局变量处理器
|
|
184
|
+
*/
|
|
185
|
+
const isGlobalVariablesHandler = (handler) => {
|
|
186
|
+
return ['backendVariables', 'frontendVariables'].includes(handler.type);
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* 类型守卫:判断是否为主题处理器
|
|
190
|
+
*/
|
|
191
|
+
const isThemeHandler = (handler) => {
|
|
192
|
+
return handler.type === 'theme';
|
|
193
|
+
};
|
|
154
194
|
/**
|
|
155
195
|
* 创建不同类型处理器的工厂函数
|
|
156
|
-
* 为每种类型(entity、structure、enum、logic、view)提供统一的操作接口
|
|
196
|
+
* 为每种类型(entity、structure、enum、logic、view、backendVariables、frontendVariables)提供统一的操作接口
|
|
157
197
|
* @param appInstance - 解析后的应用实例,用于查找新创建的对象
|
|
158
198
|
* @param app - 目前已有的应用实例,用于查找已存在的对象和添加新对象
|
|
159
199
|
* @returns 包含各种类型处理器的对象
|
|
160
200
|
*/
|
|
161
201
|
const createTypeHandlers = (appInstance, app) => ({
|
|
162
202
|
entity: {
|
|
203
|
+
type: 'entity',
|
|
163
204
|
/** 实体在应用中的路径模板 */
|
|
164
205
|
path: 'app.dataSources[name=defaultDS].entities[0]',
|
|
165
206
|
/** 从解析实例中查找实体的方法 */
|
|
166
207
|
findMethod: (name) => appInstance?.dataSources?.[0]?.findEntityByName(name),
|
|
167
208
|
/** 从目标应用中查找已存在实体的方法 */
|
|
168
209
|
findExisting: (name) => app.dataSources?.[0]?.findEntityByName(name),
|
|
169
|
-
/** 向目标应用添加实体的方法 */
|
|
170
|
-
addMethod: (json) => app.dataSources[0].addEntity(json),
|
|
171
210
|
},
|
|
172
211
|
structure: {
|
|
212
|
+
type: 'structure',
|
|
173
213
|
/** 结构体在应用中的路径模板 */
|
|
174
214
|
path: 'app.structures[0]',
|
|
175
215
|
/** 从解析实例中查找结构体的方法 */
|
|
176
216
|
findMethod: (name) => appInstance?.findStructureByName(name),
|
|
177
217
|
/** 从目标应用中查找已存在结构体的方法 */
|
|
178
218
|
findExisting: (name) => app.findStructureByName(name),
|
|
179
|
-
/** 向目标应用添加结构体的方法 */
|
|
180
|
-
addMethod: (json) => app.addStructure(json),
|
|
181
219
|
},
|
|
182
220
|
enum: {
|
|
221
|
+
type: 'enum',
|
|
183
222
|
/** 枚举在应用中的路径模板 */
|
|
184
223
|
path: 'app.enums[0]',
|
|
185
224
|
/** 从解析实例中查找枚举的方法 */
|
|
186
225
|
findMethod: (name) => appInstance?.findEnumByName(name),
|
|
187
226
|
/** 从目标应用中查找已存在枚举的方法 */
|
|
188
227
|
findExisting: (name) => app.findEnumByName(name),
|
|
189
|
-
/** 向目标应用添加枚举的方法 */
|
|
190
|
-
addMethod: (json) => app.addEnum(json),
|
|
191
228
|
},
|
|
192
229
|
logic: {
|
|
230
|
+
type: 'logic',
|
|
193
231
|
/** 逻辑在应用中的路径模板 */
|
|
194
232
|
path: 'app.logics[0]',
|
|
195
233
|
/** 从解析实例中查找逻辑的方法 */
|
|
196
234
|
findMethod: (name) => appInstance?.findLogicByName(name),
|
|
197
235
|
/** 从目标应用中查找已存在逻辑的方法 */
|
|
198
236
|
findExisting: (name) => app.findLogicByName(name),
|
|
199
|
-
/** 向目标应用添加逻辑的方法 */
|
|
200
|
-
addMethod: (json) => app.addLogic(json),
|
|
201
237
|
},
|
|
202
238
|
view: {
|
|
239
|
+
type: 'view',
|
|
203
240
|
/** 视图在应用中的路径模板 */
|
|
204
241
|
path: 'app.frontendTypes[name=pc].frontends[name=pc].views[0]',
|
|
205
242
|
/** 从解析实例中查找视图的方法 */
|
|
206
243
|
findMethod: createViewFinder(appInstance),
|
|
207
244
|
/** 从目标应用中查找已存在视图的方法 */
|
|
208
245
|
findExisting: createViewFinder(app),
|
|
209
|
-
/** 向目标应用添加视图的方法 */
|
|
210
|
-
addMethod: (json) => app.frontendTypes[0].frontends[0].addView(json),
|
|
211
246
|
},
|
|
247
|
+
backendVariables: {
|
|
248
|
+
type: 'backendVariables',
|
|
249
|
+
/** 后端全局变量在应用中的路径 */
|
|
250
|
+
path: 'app.backend.variables[0]',
|
|
251
|
+
/** 从解析实例中查找后端变量 */
|
|
252
|
+
findMethod: (name) => {
|
|
253
|
+
if (!name)
|
|
254
|
+
return appInstance?.backend;
|
|
255
|
+
const path = `app.backend.variables[name=${name}]`;
|
|
256
|
+
return appInstance?.findNodeByPath(path);
|
|
257
|
+
},
|
|
258
|
+
/** 从目标应用中查找已存在的后端变量 */
|
|
259
|
+
findExisting: (name) => {
|
|
260
|
+
if (!name)
|
|
261
|
+
return app.backend;
|
|
262
|
+
const path = `app.backend.variables[name=${name}]`;
|
|
263
|
+
return app.findNodeByPath(path);
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
frontendVariables: {
|
|
267
|
+
type: 'frontendVariables',
|
|
268
|
+
/** 前端全局变量在应用中的路径 */
|
|
269
|
+
path: 'app.frontendTypes[name=pc].frontends[name=pc].variables[0]',
|
|
270
|
+
/** 从解析实例中查找前端变量 */
|
|
271
|
+
findMethod: (name) => {
|
|
272
|
+
if (!name)
|
|
273
|
+
return appInstance?.frontendTypes?.[0]?.frontends?.[0];
|
|
274
|
+
const path = `app.frontendTypes[name=pc].frontends[name=pc].variables[name=${name}]`;
|
|
275
|
+
return appInstance?.findNodeByPath(path);
|
|
276
|
+
},
|
|
277
|
+
/** 从目标应用中查找已存在的前端变量 */
|
|
278
|
+
findExisting: (name) => {
|
|
279
|
+
if (!name)
|
|
280
|
+
return app.frontendTypes?.[0]?.frontends?.[0];
|
|
281
|
+
const path = `app.frontendTypes[name=pc].frontends[name=pc].variables[name=${name}]`;
|
|
282
|
+
return app.findNodeByPath(path);
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
theme: {
|
|
286
|
+
type: 'theme',
|
|
287
|
+
/** 主题在应用中的路径 */
|
|
288
|
+
path: 'app.frontendTypes[name=pc].frontends[name=pc].theme',
|
|
289
|
+
/** 从解析实例中查找主题 */
|
|
290
|
+
findMethod: () => appInstance?.frontendTypes?.[0]?.frontends?.[0]?.theme,
|
|
291
|
+
/** 从目标应用中查找已存在的主题 */
|
|
292
|
+
findExisting: () => app.frontendTypes?.[0]?.frontends?.[0]?.theme,
|
|
293
|
+
},
|
|
294
|
+
// 后续需要扩展 extensions 的
|
|
212
295
|
});
|
|
296
|
+
/**
|
|
297
|
+
* 通用删除操作处理
|
|
298
|
+
* @param params - 删除操作参数
|
|
299
|
+
*/
|
|
300
|
+
const handleDeleteOperation = (params) => {
|
|
301
|
+
const { handler, itemName, itemType, batchActions, errors, parentViewNames, errorMessageTemplate = `要删除的对象不存在: ${itemName}`, } = params;
|
|
302
|
+
// 根据 handler 类型调用不同的 findExisting 方法
|
|
303
|
+
let existingNode;
|
|
304
|
+
if (isViewHandler(handler)) {
|
|
305
|
+
existingNode = handler.findExisting(itemName, parentViewNames);
|
|
306
|
+
}
|
|
307
|
+
else if (isSingleNodeHandler(handler)) {
|
|
308
|
+
existingNode = handler.findExisting(itemName);
|
|
309
|
+
}
|
|
310
|
+
else if (isGlobalVariablesHandler(handler)) {
|
|
311
|
+
existingNode = handler.findExisting(itemName);
|
|
312
|
+
}
|
|
313
|
+
if (existingNode) {
|
|
314
|
+
batchActions.actions.push({
|
|
315
|
+
action: 'delete',
|
|
316
|
+
path: existingNode.nodePath,
|
|
317
|
+
object: null,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
errors.push({
|
|
322
|
+
item: itemName,
|
|
323
|
+
type: itemType,
|
|
324
|
+
error: new Error(errorMessageTemplate),
|
|
325
|
+
level: 1,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* 处理变量创建或更新操作
|
|
331
|
+
*/
|
|
332
|
+
const handleVariableCreateOrUpdate = (handler, variable, variableName, batchActions) => {
|
|
333
|
+
const existingVar = handler.findExisting(variableName);
|
|
334
|
+
const variableObject = variable.toJSON();
|
|
335
|
+
if (existingVar) {
|
|
336
|
+
// 更新操作
|
|
337
|
+
batchActions.actions.push({
|
|
338
|
+
action: 'update',
|
|
339
|
+
path: existingVar.nodePath,
|
|
340
|
+
object: variableObject,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
// 创建操作
|
|
345
|
+
batchActions.actions.push({
|
|
346
|
+
action: 'create',
|
|
347
|
+
path: handler.path,
|
|
348
|
+
object: variableObject,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
/**
|
|
353
|
+
* 处理全局变量类型(一个文件对应多个节点)
|
|
354
|
+
*/
|
|
355
|
+
const processGlobalVariables = (params) => {
|
|
356
|
+
const { handler, action, itemType, batchActions, errors } = params;
|
|
357
|
+
// 系统保留变量,不允许删除、创建或更新
|
|
358
|
+
const RESERVED_VARIABLES = new Set(['httpRequest', 'httpResponse', 'currentUser']);
|
|
359
|
+
// 获取容器节点(backend 或 frontend)
|
|
360
|
+
const container = handler.findMethod();
|
|
361
|
+
if (!container || !container.variables) {
|
|
362
|
+
errors.push({
|
|
363
|
+
item: itemType,
|
|
364
|
+
type: itemType,
|
|
365
|
+
error: new Error(`未找到${itemType}容器或variables属性`),
|
|
366
|
+
level: 2,
|
|
367
|
+
});
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
// 处理删除操作的特殊情况
|
|
371
|
+
if (action === 'delete') {
|
|
372
|
+
for (const variable of container.variables) {
|
|
373
|
+
const variableName = variable.name;
|
|
374
|
+
if (!variableName || RESERVED_VARIABLES.has(variableName))
|
|
375
|
+
continue;
|
|
376
|
+
try {
|
|
377
|
+
handleDeleteOperation({
|
|
378
|
+
handler,
|
|
379
|
+
itemName: variableName,
|
|
380
|
+
itemType,
|
|
381
|
+
batchActions,
|
|
382
|
+
errors,
|
|
383
|
+
errorMessageTemplate: `要删除的变量不存在: ${variableName}`,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
errors.push({
|
|
388
|
+
item: variableName,
|
|
389
|
+
type: itemType,
|
|
390
|
+
error: error,
|
|
391
|
+
level: 3,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
// 处理创建和更新操作
|
|
398
|
+
// 一次性获取现有容器,避免重复调用
|
|
399
|
+
const existingContainer = handler.findExisting();
|
|
400
|
+
// 构建新变量名的 Set(只在有现有容器时才需要)
|
|
401
|
+
const newVariableNames = existingContainer?.variables ? new Set() : null;
|
|
402
|
+
// 第一遍:处理新变量的创建/更新
|
|
403
|
+
for (const variable of container.variables) {
|
|
404
|
+
const variableName = variable.name;
|
|
405
|
+
if (!variableName || RESERVED_VARIABLES.has(variableName))
|
|
406
|
+
continue;
|
|
407
|
+
// 同时收集变量名(避免额外的 map 操作)
|
|
408
|
+
newVariableNames?.add(variableName);
|
|
409
|
+
try {
|
|
410
|
+
handleVariableCreateOrUpdate(handler, variable, variableName, batchActions);
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
errors.push({
|
|
414
|
+
item: variableName,
|
|
415
|
+
type: itemType,
|
|
416
|
+
error: error,
|
|
417
|
+
level: 3,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// 第二遍:处理需要删除的旧变量(app 中有但 appInstance 中没有的,目前以 spec 生成为准,如果后续不需要,这块可以隐藏)
|
|
422
|
+
if (existingContainer?.variables && newVariableNames) {
|
|
423
|
+
for (const existingVariable of existingContainer.variables) {
|
|
424
|
+
const existingVariableName = existingVariable.name;
|
|
425
|
+
if (!existingVariableName ||
|
|
426
|
+
RESERVED_VARIABLES.has(existingVariableName) ||
|
|
427
|
+
newVariableNames.has(existingVariableName)) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
// 变量在 appInstance 中不存在,按 delete 处理
|
|
431
|
+
try {
|
|
432
|
+
handleDeleteOperation({
|
|
433
|
+
handler,
|
|
434
|
+
itemName: existingVariableName,
|
|
435
|
+
itemType,
|
|
436
|
+
batchActions,
|
|
437
|
+
errors,
|
|
438
|
+
errorMessageTemplate: `要删除的变量不存在: ${existingVariableName}`,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
errors.push({
|
|
443
|
+
item: existingVariableName,
|
|
444
|
+
type: itemType,
|
|
445
|
+
error: error,
|
|
446
|
+
level: 3,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
/**
|
|
453
|
+
* 计算 view 类型的路径
|
|
454
|
+
*/
|
|
455
|
+
const calculateViewPath = (naslObject) => {
|
|
456
|
+
let type = 'views';
|
|
457
|
+
if (naslObject.parentNode?.concept === 'View') {
|
|
458
|
+
type = 'children';
|
|
459
|
+
}
|
|
460
|
+
const parentChildren = naslObject.parentNode?.[type] || [];
|
|
461
|
+
const childIndex = parentChildren.findIndex((child) => child === naslObject);
|
|
462
|
+
const index = childIndex !== -1 ? childIndex : 0;
|
|
463
|
+
return `${naslObject.parentNode.nodePath}.${type}[${index}]`;
|
|
464
|
+
};
|
|
465
|
+
/**
|
|
466
|
+
* 处理 view 类型的 children 属性
|
|
467
|
+
*/
|
|
468
|
+
const handleViewChildren = (object, action) => {
|
|
469
|
+
if (action === 'create') {
|
|
470
|
+
// 对于创建操作,需要将 children 数组转换为空数组
|
|
471
|
+
object.children = [];
|
|
472
|
+
}
|
|
473
|
+
else if (action === 'update') {
|
|
474
|
+
// 对于更新操作,删除 children 属性
|
|
475
|
+
if (object.children) {
|
|
476
|
+
delete object.children;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* 处理单节点创建或更新操作
|
|
482
|
+
*/
|
|
483
|
+
const handleSingleNodeCreateOrUpdate = (handler, itemName, itemType, parentViewNames, batchActions, errors) => {
|
|
484
|
+
// 从解析的实例中获取 NASL 对象
|
|
485
|
+
let naslObject;
|
|
486
|
+
// 根据 handler 类型调用不同的 findMethod
|
|
487
|
+
if (isViewHandler(handler)) {
|
|
488
|
+
naslObject = handler.findMethod(itemName, parentViewNames);
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
naslObject = handler.findMethod(itemName);
|
|
492
|
+
}
|
|
493
|
+
if (!naslObject) {
|
|
494
|
+
errors.push({
|
|
495
|
+
item: itemName,
|
|
496
|
+
type: itemType,
|
|
497
|
+
error: new Error(`未找到对应的 NASL 对象: ${handler.path} - ${itemName}`),
|
|
498
|
+
level: 2,
|
|
499
|
+
});
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
let finalAction = 'create';
|
|
503
|
+
let path = '';
|
|
504
|
+
let object = naslObject.toJSON();
|
|
505
|
+
// 特殊处理:对于 view 类型,计算正确的路径
|
|
506
|
+
if (itemType === 'view' && naslObject.parentNode) {
|
|
507
|
+
path = calculateViewPath(naslObject);
|
|
508
|
+
}
|
|
509
|
+
// 检查目标应用中是否已存在同名对象
|
|
510
|
+
let existingNode;
|
|
511
|
+
if (isViewHandler(handler)) {
|
|
512
|
+
existingNode = handler.findExisting(itemName, parentViewNames);
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
existingNode = handler.findExisting(itemName);
|
|
516
|
+
}
|
|
517
|
+
if (existingNode) {
|
|
518
|
+
// 存在同名对象,标记为更新操作
|
|
519
|
+
finalAction = 'update';
|
|
520
|
+
path = existingNode.nodePath;
|
|
521
|
+
}
|
|
522
|
+
// 特殊处理 view 类型的 children 属性
|
|
523
|
+
if (itemType === 'view') {
|
|
524
|
+
handleViewChildren(object, finalAction);
|
|
525
|
+
}
|
|
526
|
+
// 添加创建或更新操作到批量操作列表
|
|
527
|
+
batchActions.actions.push({
|
|
528
|
+
action: finalAction,
|
|
529
|
+
path: path || handler.path,
|
|
530
|
+
object,
|
|
531
|
+
});
|
|
532
|
+
};
|
|
533
|
+
/**
|
|
534
|
+
* 处理单节点类型(一个文件对应一个节点)
|
|
535
|
+
*/
|
|
536
|
+
const processSingleNode = (params) => {
|
|
537
|
+
const { handler, itemName, itemType, action, parentViewNames, batchActions, errors } = params;
|
|
538
|
+
if (action === 'delete') {
|
|
539
|
+
// 删除操作:使用通用删除方法
|
|
540
|
+
handleDeleteOperation({
|
|
541
|
+
handler,
|
|
542
|
+
itemName,
|
|
543
|
+
itemType,
|
|
544
|
+
batchActions,
|
|
545
|
+
errors,
|
|
546
|
+
parentViewNames,
|
|
547
|
+
errorMessageTemplate: `要删除的对象不存在: ${handler.path} - ${itemName}`,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
// 创建或更新操作
|
|
552
|
+
handleSingleNodeCreateOrUpdate(handler, itemName, itemType, parentViewNames, batchActions, errors);
|
|
553
|
+
}
|
|
554
|
+
};
|
|
213
555
|
/**
|
|
214
556
|
* 检测并生成缺失的父级视图模板
|
|
215
557
|
* @param path - 当前项的路径
|
|
@@ -256,14 +598,37 @@ const transformCodeAction = ({ path, content, action, }) => {
|
|
|
256
598
|
// 移除路径前缀 'src/',并解析命名空间
|
|
257
599
|
const namespace = path.replace(/^src\//, '');
|
|
258
600
|
const namespaceParts = namespace.split('.');
|
|
259
|
-
//
|
|
260
|
-
const itemName = namespaceParts.length >= 2 ? namespaceParts[namespaceParts.length - 2] : '';
|
|
261
|
-
const parentNamespace = namespaceParts.slice(0, -2).join('.');
|
|
262
|
-
// 根据命名空间确定项目类型
|
|
601
|
+
// 根据命名空间确定项目类型(需要先确定类型)
|
|
263
602
|
const itemType = getItemType(namespace);
|
|
603
|
+
// 提取项目名称和父命名空间,对于变量类型需要特殊处理
|
|
604
|
+
let itemName;
|
|
605
|
+
let parentNamespace;
|
|
606
|
+
if (itemType === 'backendVariables' || itemType === 'frontendVariables') {
|
|
607
|
+
// 对于变量类型,路径格式为: app.backend.variables.ts 或 app.frontendTypes.pc.frontends.pc.variables.ts
|
|
608
|
+
// itemName 设为空字符串(因为这是整个 variables 文件)
|
|
609
|
+
// parentNamespace 为去掉 .ts 的部分,即保留到 variables
|
|
610
|
+
itemName = '';
|
|
611
|
+
parentNamespace = namespaceParts.slice(0, -1).join('.');
|
|
612
|
+
}
|
|
613
|
+
else if (itemType === 'theme') {
|
|
614
|
+
// 对于主题类型,路径格式为: app.frontendTypes.pc.frontends.pc.theme.css
|
|
615
|
+
itemName = '';
|
|
616
|
+
parentNamespace = namespaceParts.slice(0, -1).join('.');
|
|
617
|
+
}
|
|
618
|
+
else if (itemType === 'extensions') {
|
|
619
|
+
parentNamespace = namespaceParts.slice(0, -1).join('.');
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
// 其他类型使用原有逻辑,即单文件模式(例如一个实体对应一个文件)
|
|
623
|
+
itemName = namespaceParts.length >= 2 ? namespaceParts[namespaceParts.length - 2] : '';
|
|
624
|
+
parentNamespace = namespaceParts.slice(0, -2).join('.');
|
|
625
|
+
}
|
|
264
626
|
// 生成转换后的代码,为非未知类型添加命名空间包装
|
|
265
627
|
let transformedCode = content;
|
|
266
|
-
if (itemType
|
|
628
|
+
if (itemType === 'theme') {
|
|
629
|
+
transformedCode = `namespace ${parentNamespace} {\n $theme\`${content}\`;\n}\n`;
|
|
630
|
+
}
|
|
631
|
+
else if (itemType !== 'unknown' && parentNamespace) {
|
|
267
632
|
// 对于视图类型,需要额外的缩进
|
|
268
633
|
const indent = itemType === 'view' ? ' ' : '';
|
|
269
634
|
transformedCode = `namespace ${parentNamespace} {\n${indent}${content}\n}\n`;
|
|
@@ -311,8 +676,10 @@ const transform2BatchActions = ({ codeActions, app, sessionId, type = 'normal',
|
|
|
311
676
|
// 第一阶段:转换所有codeAction
|
|
312
677
|
for (const item of codeActions) {
|
|
313
678
|
try {
|
|
314
|
-
//
|
|
315
|
-
if (!item.path.endsWith('.ts') &&
|
|
679
|
+
// 检查文件后缀,目前只处理 .ts、.tsx 和 .css 文件
|
|
680
|
+
if (!item.path.endsWith('.ts') &&
|
|
681
|
+
!item.path.endsWith('.tsx') &&
|
|
682
|
+
!item.path.endsWith('.css')) {
|
|
316
683
|
continue;
|
|
317
684
|
}
|
|
318
685
|
if (!(0, nasl_file_types_1.isKnownFileType)(item.path)) {
|
|
@@ -324,6 +691,9 @@ const transform2BatchActions = ({ codeActions, app, sessionId, type = 'normal',
|
|
|
324
691
|
}
|
|
325
692
|
// 处理当前项
|
|
326
693
|
const { transformedCode, itemType, itemName, action, parentViewNames } = transformCodeAction(item);
|
|
694
|
+
// 跳过未知类型的 codeAction/暂时禁用 extensions
|
|
695
|
+
if (itemType === 'unknown' || itemType === 'extensions')
|
|
696
|
+
continue;
|
|
327
697
|
// 只有非删除操作才需要累积代码用于解析,因为删除操作不需要解析新的 NASL 对象
|
|
328
698
|
if (action !== 'delete') {
|
|
329
699
|
codeArray.push(transformedCode);
|
|
@@ -376,84 +746,62 @@ const transform2BatchActions = ({ codeActions, app, sessionId, type = 'normal',
|
|
|
376
746
|
const typeHandlers = createTypeHandlers(appInstance, app);
|
|
377
747
|
// 遍历每个项目,生成对应的批量操作
|
|
378
748
|
for (const { type: itemType, name: itemName, action, parentViewNames } of itemTypes) {
|
|
379
|
-
//
|
|
380
|
-
if (itemType === 'unknown' || !itemName)
|
|
749
|
+
// 跳过未知类型,或者非可选名称但无名称的项目
|
|
750
|
+
if (itemType === 'unknown' || (!isNameOptional(itemType) && !itemName))
|
|
381
751
|
continue;
|
|
382
752
|
try {
|
|
383
753
|
const handler = typeHandlers[itemType];
|
|
384
754
|
if (!handler)
|
|
385
755
|
continue;
|
|
386
|
-
//
|
|
387
|
-
if (
|
|
388
|
-
//
|
|
389
|
-
const
|
|
390
|
-
if (
|
|
391
|
-
|
|
756
|
+
// 使用类型守卫进行精确的类型判断和处理
|
|
757
|
+
if (isThemeHandler(handler)) {
|
|
758
|
+
// 主题类型:无需参数
|
|
759
|
+
const naslObject = handler.findMethod();
|
|
760
|
+
if (naslObject) {
|
|
761
|
+
const existingNode = handler.findExisting();
|
|
392
762
|
batchActions.actions.push({
|
|
393
|
-
action: '
|
|
394
|
-
path: existingNode.
|
|
395
|
-
object:
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
errors.push({
|
|
401
|
-
item: itemName,
|
|
402
|
-
type: itemType,
|
|
403
|
-
error: new Error(`要删除的对象不存在: ${handler.path} - ${itemName}`),
|
|
404
|
-
level: 1,
|
|
763
|
+
action: 'update',
|
|
764
|
+
path: existingNode?.nodePath || handler.path,
|
|
765
|
+
object: {
|
|
766
|
+
// 暂时只支持 scopeVariableMap
|
|
767
|
+
...existingNode.toJSON(),
|
|
768
|
+
scopeVariableMap: naslObject.scopeVariableMap,
|
|
769
|
+
},
|
|
405
770
|
});
|
|
406
771
|
}
|
|
407
772
|
}
|
|
408
|
-
else {
|
|
409
|
-
//
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
path = existingNode.nodePath;
|
|
441
|
-
}
|
|
442
|
-
if (itemType === 'view') {
|
|
443
|
-
if (finalAction === 'create') {
|
|
444
|
-
// 对于创建操作,需要将 children 数组转换为对象
|
|
445
|
-
object.children = [];
|
|
446
|
-
}
|
|
447
|
-
if (finalAction === 'update') {
|
|
448
|
-
if (object.children)
|
|
449
|
-
delete object.children;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
// 添加创建或更新操作到批量操作列表
|
|
453
|
-
batchActions.actions.push({
|
|
454
|
-
action: finalAction,
|
|
455
|
-
path: path || handler.path,
|
|
456
|
-
object,
|
|
773
|
+
else if (isGlobalVariablesHandler(handler)) {
|
|
774
|
+
// 全局变量类型:一个文件对应多个节点
|
|
775
|
+
processGlobalVariables({
|
|
776
|
+
handler,
|
|
777
|
+
action,
|
|
778
|
+
itemType,
|
|
779
|
+
batchActions,
|
|
780
|
+
errors,
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
else if (isViewHandler(handler)) {
|
|
784
|
+
// 视图类型:一个文件对应一个节点,支持嵌套视图
|
|
785
|
+
processSingleNode({
|
|
786
|
+
handler,
|
|
787
|
+
itemName,
|
|
788
|
+
itemType,
|
|
789
|
+
action,
|
|
790
|
+
parentViewNames,
|
|
791
|
+
batchActions,
|
|
792
|
+
errors,
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
else if (isSingleNodeHandler(handler)) {
|
|
796
|
+
// 单节点类型:entity, structure, enum, logic
|
|
797
|
+
processSingleNode({
|
|
798
|
+
handler,
|
|
799
|
+
itemName,
|
|
800
|
+
itemType,
|
|
801
|
+
action,
|
|
802
|
+
parentViewNames,
|
|
803
|
+
batchActions,
|
|
804
|
+
errors,
|
|
457
805
|
});
|
|
458
806
|
}
|
|
459
807
|
}
|
|
@@ -467,6 +815,10 @@ const transform2BatchActions = ({ codeActions, app, sessionId, type = 'normal',
|
|
|
467
815
|
});
|
|
468
816
|
}
|
|
469
817
|
}
|
|
818
|
+
// 第五阶段:同步 directories(只处理 appInstance 中已存在的)
|
|
819
|
+
if (appInstance && app) {
|
|
820
|
+
handleAppDirectories(appInstance, app, batchActions, errors);
|
|
821
|
+
}
|
|
470
822
|
// 第四阶段:错误处理和结果返回
|
|
471
823
|
// 如果有错误发生,在控制台输出错误信息(但不中断执行)
|
|
472
824
|
if (errors.length > 0) {
|
|
@@ -491,6 +843,307 @@ const transform2BatchActions = ({ codeActions, app, sessionId, type = 'normal',
|
|
|
491
843
|
return result;
|
|
492
844
|
};
|
|
493
845
|
exports.transform2BatchActions = transform2BatchActions;
|
|
846
|
+
/**
|
|
847
|
+
* 构建全局 definition 索引
|
|
848
|
+
* 一次性构建,避免重复遍历
|
|
849
|
+
* @param allDirs - 所有目录数组
|
|
850
|
+
* @returns 全局索引对象
|
|
851
|
+
*/
|
|
852
|
+
const buildGlobalDefIndex = (allDirs) => {
|
|
853
|
+
const defMap = new Map();
|
|
854
|
+
const dirDefMap = new Map();
|
|
855
|
+
for (const dir of allDirs) {
|
|
856
|
+
if (!dir?.definitions || !dir.name)
|
|
857
|
+
continue;
|
|
858
|
+
const defKeys = new Set();
|
|
859
|
+
for (const def of dir.definitions) {
|
|
860
|
+
if (!def)
|
|
861
|
+
continue;
|
|
862
|
+
const key = `${def.namespace}:${def.name}`;
|
|
863
|
+
defMap.set(key, {
|
|
864
|
+
def,
|
|
865
|
+
dirName: dir.name,
|
|
866
|
+
dirPath: dir.nodePath,
|
|
867
|
+
});
|
|
868
|
+
defKeys.add(key);
|
|
869
|
+
}
|
|
870
|
+
if (defKeys.size > 0) {
|
|
871
|
+
dirDefMap.set(dir.name, defKeys);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return { defMap, dirDefMap };
|
|
875
|
+
};
|
|
876
|
+
/**
|
|
877
|
+
* 深度比较两个 definition 是否需要更新
|
|
878
|
+
* 只比较关键属性,避免不必要的序列化
|
|
879
|
+
* @param appDef - app 中的 definition
|
|
880
|
+
* @param instanceDef - appInstance 中的 definition
|
|
881
|
+
* @returns 是否需要更新
|
|
882
|
+
*/
|
|
883
|
+
const needsDefinitionUpdate = (appDef, instanceDef) => {
|
|
884
|
+
// 快速检查:引用相等
|
|
885
|
+
if (appDef === instanceDef)
|
|
886
|
+
return false;
|
|
887
|
+
// 关键属性比较
|
|
888
|
+
return (appDef.referredConcept !== instanceDef.referredConcept ||
|
|
889
|
+
appDef.namespace !== instanceDef.namespace ||
|
|
890
|
+
appDef.name !== instanceDef.name);
|
|
891
|
+
};
|
|
892
|
+
/**
|
|
893
|
+
* 同步目录的 definitions
|
|
894
|
+
* 使用 Map 替代数组查找,使用全局索引避免重复遍历
|
|
895
|
+
* @param instanceDir - appInstance 中的目录
|
|
896
|
+
* @param appDir - app 中对应的目录
|
|
897
|
+
* @param globalDefIndex - 全局 definition 索引
|
|
898
|
+
* @param pendingActions - 待添加的批量操作数组
|
|
899
|
+
* @param errors - 错误收集数组
|
|
900
|
+
*/
|
|
901
|
+
const syncDirectoryDefinitions = (instanceDir, appDir, globalDefIndex, pendingActions, errors) => {
|
|
902
|
+
try {
|
|
903
|
+
// 快速检查
|
|
904
|
+
if (!instanceDir?.definitions || instanceDir.definitions.length === 0) {
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
// 构建当前目录的 definition Map
|
|
908
|
+
const appDefMap = new Map((appDir?.definitions || []).map((def) => [`${def.namespace}:${def.name}`, def]));
|
|
909
|
+
for (const instanceDef of instanceDir.definitions) {
|
|
910
|
+
if (!instanceDef)
|
|
911
|
+
continue;
|
|
912
|
+
const key = `${instanceDef.namespace}:${instanceDef.name}`;
|
|
913
|
+
const defObject = instanceDef.toJSON();
|
|
914
|
+
// 第一步:在当前目录查找
|
|
915
|
+
const appDefInCurrentDir = appDefMap.get(key);
|
|
916
|
+
if (appDefInCurrentDir) {
|
|
917
|
+
// 在当前目录找到了,检查是否需要更新
|
|
918
|
+
if (needsDefinitionUpdate(appDefInCurrentDir, instanceDef)) {
|
|
919
|
+
// 内容不同,更新(先删除再创建)
|
|
920
|
+
pendingActions.push({
|
|
921
|
+
action: 'delete',
|
|
922
|
+
path: appDefInCurrentDir.nodePath,
|
|
923
|
+
object: null,
|
|
924
|
+
}, {
|
|
925
|
+
action: 'create',
|
|
926
|
+
path: `${appDir.nodePath}.definitions[0]`,
|
|
927
|
+
object: defObject,
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
// 内容相同则跳过
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
// 第二步:使用全局索引查找其他目录
|
|
934
|
+
const defInOtherDir = globalDefIndex.defMap.get(key);
|
|
935
|
+
if (defInOtherDir && defInOtherDir.dirName !== appDir.name) {
|
|
936
|
+
// 在其他文件夹找到了 → 文件移动场景
|
|
937
|
+
// 先删除旧位置的引用
|
|
938
|
+
pendingActions.push({
|
|
939
|
+
action: 'delete',
|
|
940
|
+
path: defInOtherDir.def.nodePath,
|
|
941
|
+
object: null,
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
// 然后在当前目录创建新引用(无论是移动还是新建)
|
|
945
|
+
pendingActions.push({
|
|
946
|
+
action: 'create',
|
|
947
|
+
path: `${appDir.nodePath}.definitions[0]`,
|
|
948
|
+
object: defObject,
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
catch (error) {
|
|
954
|
+
errors.push({
|
|
955
|
+
item: instanceDir?.name || 'unknown',
|
|
956
|
+
type: 'definitionSync',
|
|
957
|
+
error: error,
|
|
958
|
+
level: 4,
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
/**
|
|
963
|
+
* 删除新目录中的 definitions 在其他目录中的旧引用
|
|
964
|
+
* 使用全局索引,避免重复构建 Map
|
|
965
|
+
* @param instanceDir - 新目录
|
|
966
|
+
* @param globalDefIndex - 全局 definition 索引
|
|
967
|
+
* @param pendingActions - 待添加的批量操作数组
|
|
968
|
+
*/
|
|
969
|
+
const deleteOldDefinitionReferences = (instanceDir, globalDefIndex, pendingActions) => {
|
|
970
|
+
if (!instanceDir?.definitions || instanceDir.definitions.length === 0) {
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
// 直接使用全局索引查找
|
|
974
|
+
for (const instanceDef of instanceDir.definitions) {
|
|
975
|
+
if (!instanceDef)
|
|
976
|
+
continue;
|
|
977
|
+
const key = `${instanceDef.namespace}:${instanceDef.name}`;
|
|
978
|
+
const oldDef = globalDefIndex.defMap.get(key);
|
|
979
|
+
if (oldDef) {
|
|
980
|
+
// 找到了旧引用,删除它
|
|
981
|
+
pendingActions.push({
|
|
982
|
+
action: 'delete',
|
|
983
|
+
path: oldDef.def.nodePath,
|
|
984
|
+
object: null,
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
/**
|
|
990
|
+
* 同步单个目录
|
|
991
|
+
* 通用的目录同步逻辑,适用于所有类型的目录
|
|
992
|
+
* @param instanceDir - appInstance 中的目录
|
|
993
|
+
* @param appDir - app 中对应的目录(可能不存在)
|
|
994
|
+
* @param globalDefIndex - 全局 definition 索引
|
|
995
|
+
* @param parentPath - 父级路径
|
|
996
|
+
* @param pendingActions - 待添加的批量操作数组
|
|
997
|
+
* @param errors - 错误收集数组
|
|
998
|
+
* @param errorType - 错误类型标识
|
|
999
|
+
*/
|
|
1000
|
+
const syncSingleDirectory = (instanceDir, appDir, globalDefIndex, parentPath, pendingActions, errors, errorType) => {
|
|
1001
|
+
try {
|
|
1002
|
+
if (!appDir) {
|
|
1003
|
+
// 目录不存在,需要创建
|
|
1004
|
+
// 先删除该目录中 definitions 在其他目录的旧引用
|
|
1005
|
+
deleteOldDefinitionReferences(instanceDir, globalDefIndex, pendingActions);
|
|
1006
|
+
// 然后创建完整的新目录
|
|
1007
|
+
const dirObject = instanceDir.toJSON();
|
|
1008
|
+
pendingActions.push({
|
|
1009
|
+
action: 'create',
|
|
1010
|
+
path: parentPath,
|
|
1011
|
+
object: dirObject,
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
else {
|
|
1015
|
+
// 目录存在,同步 definitions
|
|
1016
|
+
syncDirectoryDefinitions(instanceDir, appDir, globalDefIndex, pendingActions, errors);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
catch (error) {
|
|
1020
|
+
errors.push({
|
|
1021
|
+
item: instanceDir?.name || 'unknown',
|
|
1022
|
+
type: errorType,
|
|
1023
|
+
error: error,
|
|
1024
|
+
level: 4,
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
/**
|
|
1029
|
+
* 处理 app directories 同步
|
|
1030
|
+
* 处理四种 directories: 'LOGICS', 'ENUMS', 'STRUCTURES' 以及 appInstance.dataSources[0] 的 directories
|
|
1031
|
+
* 比对 directories 及引用 definitions,最小粒度是 definition,唯一标识是 namespace+name
|
|
1032
|
+
* 文件夹只做新增更新,definition 如果在同一领域的文件夹下,需要先删除原引用,再创建新引用
|
|
1033
|
+
*
|
|
1034
|
+
* 性能优化:
|
|
1035
|
+
* 1. 全局 definition 索引缓存 - 避免重复构建
|
|
1036
|
+
* 3. 批量操作收集 - 减少数组扩容
|
|
1037
|
+
* 4. 提前验证和快速退出 - 避免无效处理
|
|
1038
|
+
* 5. 优化的 definition 比较 - 只比较关键属性
|
|
1039
|
+
*/
|
|
1040
|
+
const handleAppDirectories = (appInstance, app, batchActions, errors) => {
|
|
1041
|
+
// 性能计时开始
|
|
1042
|
+
const startTime = performance.now();
|
|
1043
|
+
// 1. 边界检查和快速退出
|
|
1044
|
+
if (!appInstance || !app) {
|
|
1045
|
+
errors.push({
|
|
1046
|
+
item: 'handleAppDirectories',
|
|
1047
|
+
type: 'invalidParams',
|
|
1048
|
+
error: new Error('appInstance 或 app 参数无效'),
|
|
1049
|
+
level: 5,
|
|
1050
|
+
});
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
try {
|
|
1054
|
+
// 2. 快速检查是否有需要处理的目录
|
|
1055
|
+
const hasInstanceDirs = appInstance.directories?.some((dir) => ['LOGICS', 'ENUMS', 'STRUCTURES'].includes(dir?.name) && dir.children?.length > 0);
|
|
1056
|
+
const hasDataSourceDirs = appInstance.dataSources?.[0]?.directories?.length > 0;
|
|
1057
|
+
if (!hasInstanceDirs && !hasDataSourceDirs) {
|
|
1058
|
+
console.log('[handleAppDirectories] 无需处理目录同步');
|
|
1059
|
+
return; // 无需处理
|
|
1060
|
+
}
|
|
1061
|
+
// 3. 批量操作收集器(减少数组扩容)
|
|
1062
|
+
const pendingActions = [];
|
|
1063
|
+
// 4. 处理根目录类型 (LOGICS, ENUMS, STRUCTURES)
|
|
1064
|
+
const rootDirectoryTypes = ['LOGICS', 'ENUMS', 'STRUCTURES'];
|
|
1065
|
+
for (const rootType of rootDirectoryTypes) {
|
|
1066
|
+
try {
|
|
1067
|
+
const instanceRootDir = appInstance.directories?.find((dir) => dir?.name === rootType);
|
|
1068
|
+
if (!instanceRootDir?.children?.length) {
|
|
1069
|
+
continue;
|
|
1070
|
+
}
|
|
1071
|
+
let appRootDir = app.directories?.find((dir) => dir?.name === rootType);
|
|
1072
|
+
if (!appRootDir) {
|
|
1073
|
+
// 根目录不存在,直接创建
|
|
1074
|
+
pendingActions.push({
|
|
1075
|
+
action: 'create',
|
|
1076
|
+
path: 'app.directories[0]',
|
|
1077
|
+
object: instanceRootDir.toJSON(),
|
|
1078
|
+
});
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
// 5. 构建子目录 Map(O(1) 查找,替代数组遍历)
|
|
1082
|
+
const allAppChildDirs = appRootDir.children || [];
|
|
1083
|
+
const appChildDirMap = new Map(allAppChildDirs.filter((dir) => dir?.name).map((dir) => [dir.name, dir]));
|
|
1084
|
+
// 6. 构建全局 definition 索引(一次性构建)
|
|
1085
|
+
const globalDefIndex = buildGlobalDefIndex(allAppChildDirs);
|
|
1086
|
+
// 7. 遍历子目录
|
|
1087
|
+
for (const instanceChildDir of instanceRootDir.children) {
|
|
1088
|
+
if (!instanceChildDir?.name)
|
|
1089
|
+
continue;
|
|
1090
|
+
const appChildDir = appChildDirMap.get(instanceChildDir.name);
|
|
1091
|
+
syncSingleDirectory(instanceChildDir, appChildDir, globalDefIndex, `${appRootDir.nodePath}.children[0]`, pendingActions, errors, 'directorySync');
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
catch (error) {
|
|
1095
|
+
errors.push({
|
|
1096
|
+
item: rootType,
|
|
1097
|
+
type: 'rootDirectorySync',
|
|
1098
|
+
error: error,
|
|
1099
|
+
level: 4,
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
// 8. 处理数据源 (Entity) directories
|
|
1104
|
+
if (appInstance.dataSources?.[0]?.directories && app.dataSources?.[0]) {
|
|
1105
|
+
try {
|
|
1106
|
+
const instanceDataSource = appInstance.dataSources[0];
|
|
1107
|
+
const appDataSource = app.dataSources[0];
|
|
1108
|
+
const allAppEntityDirs = appDataSource.directories || [];
|
|
1109
|
+
// 构建目录 Map(O(1) 查找)
|
|
1110
|
+
const entityDirMap = new Map(allAppEntityDirs.filter((dir) => dir?.name).map((dir) => [dir.name, dir]));
|
|
1111
|
+
// 构建全局 definition 索引
|
|
1112
|
+
const globalDefIndex = buildGlobalDefIndex(allAppEntityDirs);
|
|
1113
|
+
for (const instanceDir of instanceDataSource.directories) {
|
|
1114
|
+
if (!instanceDir?.name)
|
|
1115
|
+
continue;
|
|
1116
|
+
const appDir = entityDirMap.get(instanceDir.name);
|
|
1117
|
+
syncSingleDirectory(instanceDir, appDir, globalDefIndex, `${appDataSource.nodePath}.directories[0]`, pendingActions, errors, 'entityDirectorySync');
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
catch (error) {
|
|
1121
|
+
errors.push({
|
|
1122
|
+
item: 'dataSources',
|
|
1123
|
+
type: 'dataSourceDirectorySync',
|
|
1124
|
+
error: error,
|
|
1125
|
+
level: 4,
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
// 9. 批量添加所有操作(一次性 push,减少数组扩容)
|
|
1130
|
+
if (pendingActions.length > 0) {
|
|
1131
|
+
batchActions.actions.push(...pendingActions);
|
|
1132
|
+
console.log(`[handleAppDirectories] 生成 ${pendingActions.length} 个批量操作`);
|
|
1133
|
+
}
|
|
1134
|
+
// 性能计时结束
|
|
1135
|
+
const endTime = performance.now();
|
|
1136
|
+
console.log(`[handleAppDirectories] 执行时间: ${(endTime - startTime).toFixed(2)}ms`);
|
|
1137
|
+
}
|
|
1138
|
+
catch (error) {
|
|
1139
|
+
errors.push({
|
|
1140
|
+
item: 'handleAppDirectories',
|
|
1141
|
+
type: 'directorySync',
|
|
1142
|
+
error: error,
|
|
1143
|
+
level: 4,
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
494
1147
|
/**
|
|
495
1148
|
* 辅助函数:从路径项中提取索引
|
|
496
1149
|
* @param pathItem - 路径项,如 'entities[0]'
|