mirror of
https://github.com/HappyLifeOk/cc-3-8-x-mcp.git
synced 2026-08-21 14:07:21 +00:00
177 lines
6.6 KiB
JavaScript
177 lines
6.6 KiB
JavaScript
// ============================================================
|
|
// editor/index.js — 声明式批量编辑 prefab 主入口
|
|
//
|
|
// editPrefab(filePath, ops[], options?)
|
|
// - 内存内依次执行所有 op
|
|
// - 自动判别 stub vs 普通节点
|
|
// - 任一 op 失败抛错、不落盘
|
|
// - dryRun: 跑完不写盘,返回字段级 diff
|
|
// ============================================================
|
|
|
|
'use strict';
|
|
|
|
const { parsePrefab } = require('../parse.js');
|
|
const { writePrefab } = require('../write.js');
|
|
const { computeDiff } = require('./diff.js');
|
|
|
|
// 各 op handler
|
|
const { execSetPosition } = require('./ops/set-position.js');
|
|
const { execSetLabelText } = require('./ops/set-label-text.js');
|
|
const { execSetSpriteFrame } = require('./ops/set-sprite-frame.js');
|
|
const { execSetActive } = require('./ops/set-active.js');
|
|
const { execSetComponentField } = require('./ops/set-component-field.js');
|
|
const { execSetComponentEnabled } = require('./ops/set-component-enabled.js');
|
|
const { execSetAnchor } = require('./ops/set-anchor.js');
|
|
const { execSetSize } = require('./ops/set-size.js');
|
|
const { execAdjustPosition } = require('./ops/adjust-position.js');
|
|
const { execRenameNode } = require('./ops/rename-node.js');
|
|
const { execReparent } = require('./ops/reparent.js');
|
|
const { execReorderChildren } = require('./ops/reorder-children.js');
|
|
const { execAddNode } = require('./ops/add-node.js');
|
|
const { execRemoveNode } = require('./ops/remove-node.js');
|
|
const { execCloneNode } = require('./ops/clone-node.js');
|
|
const { execAddComponent } = require('./ops/add-component.js');
|
|
const { execRemoveComponent } = require('./ops/remove-component.js');
|
|
const { execSetComponentRef } = require('./ops/set-component-ref.js');
|
|
const { execSetNestedComponentField } = require('./ops/set-nested-component-field.js');
|
|
const { execBulkSet } = require('./ops/bulk-set.js');
|
|
const { execDedupeComponent } = require('./ops/dedupe-component.js');
|
|
const { execSetEditBox } = require('./ops/set-editbox.js');
|
|
const { execSetLabel } = require('./ops/set-label.js');
|
|
const { execSetButton } = require('./ops/set-button.js');
|
|
const { execSetLayout } = require('./ops/set-layout.js');
|
|
const { execSetRichText } = require('./ops/set-richtext.js');
|
|
const { execSetSprite } = require('./ops/set-sprite.js');
|
|
const { execSetNodeColor } = require('./ops/set-node-color.js');
|
|
const { execReplaceNestedPrefab } = require('./ops/replace-nested-prefab.js');
|
|
const { execAddNestedPrefab } = require('./ops/add-nested-prefab.js');
|
|
const { execAddSpineSocket } = require('./ops/add-spine-socket.js');
|
|
const { execResetOverrides } = require('./ops/reset-overrides.js');
|
|
const { execEnsureMeta } = require('./ops/ensure-meta.js');
|
|
const { execSyncNestedRoots } = require('./ops/sync-nested-roots.js');
|
|
const { validateOps } = require('./op-schema.js');
|
|
|
|
const OP_HANDLERS = {
|
|
'set-position': execSetPosition,
|
|
'set-label-text': execSetLabelText,
|
|
'set-sprite-frame': execSetSpriteFrame,
|
|
'set-active': execSetActive,
|
|
'set-component-field': execSetComponentField,
|
|
'set-component-enabled': execSetComponentEnabled,
|
|
'set-anchor': execSetAnchor,
|
|
'set-size': execSetSize,
|
|
'adjust-position': execAdjustPosition,
|
|
'rename-node': execRenameNode,
|
|
'reparent': execReparent,
|
|
'reorder-children': execReorderChildren,
|
|
'add-node': execAddNode,
|
|
'remove-node': execRemoveNode,
|
|
'clone-node': execCloneNode,
|
|
'add-component': execAddComponent,
|
|
'remove-component': execRemoveComponent,
|
|
'set-component-ref': execSetComponentRef,
|
|
'set-nested-component-field': execSetNestedComponentField,
|
|
'bulk-set': execBulkSet,
|
|
'dedupe-component': execDedupeComponent,
|
|
'set-editbox': execSetEditBox,
|
|
'set-label': execSetLabel,
|
|
'set-button': execSetButton,
|
|
'set-layout': execSetLayout,
|
|
'set-richtext': execSetRichText,
|
|
'set-sprite': execSetSprite,
|
|
'set-node-color': execSetNodeColor,
|
|
'replace-nested-prefab': execReplaceNestedPrefab,
|
|
'add-nested-prefab': execAddNestedPrefab,
|
|
'add-spine-socket': execAddSpineSocket,
|
|
'reset-overrides': execResetOverrides,
|
|
'ensure-meta': execEnsureMeta,
|
|
'sync-nested-roots': execSyncNestedRoots,
|
|
};
|
|
|
|
/**
|
|
* 声明式批量编辑 prefab
|
|
*
|
|
* @param {string} filePath prefab 文件路径(读取 + 写回同一路径)
|
|
* @param {object[]} ops op 描述数组
|
|
* @param {object} [options]
|
|
* @param {string} [options.projectRoot] 项目根目录(含 assets/),默认从 filePath 向上推断。
|
|
* @param {boolean} [options.dryRun] true 时不写盘,仅返回模拟结果(含 diff)。
|
|
* @returns {{ changed: boolean, opsApplied: number, nodesAffected: (string|number)[], dryRun?: boolean, diff?: object[] }}
|
|
*
|
|
* @throws 任一 op 失败时抛错,不落盘
|
|
*/
|
|
function editPrefab(filePath, ops, options) {
|
|
if (typeof filePath !== 'string') {
|
|
throw new Error('editPrefab: filePath 必须是字符串');
|
|
}
|
|
if (!Array.isArray(ops) || ops.length === 0) {
|
|
throw new Error('editPrefab: ops 必须是非空数组');
|
|
}
|
|
|
|
// schema 预校验:跑前发现拼错的字段(comp / ref / 拼漏 op 等),不到 handler 才报错
|
|
validateOps(ops, Object.keys(OP_HANDLERS));
|
|
|
|
const opts = options || {};
|
|
|
|
const prefabData = parsePrefab(filePath);
|
|
prefabData.resolverStartPath = opts.projectRoot || filePath;
|
|
|
|
const dryRun = !!opts.dryRun;
|
|
prefabData.dryRun = dryRun;
|
|
const beforeSnapshot = dryRun
|
|
? JSON.parse(JSON.stringify(prefabData.elements))
|
|
: null;
|
|
|
|
const affectedIds = new Set();
|
|
const affectedNames = [];
|
|
|
|
let opsApplied = 0;
|
|
for (const op of ops) {
|
|
if (!op || typeof op.op !== 'string') {
|
|
throw new Error(`editPrefab: op 格式错误(缺少 op 字段): ${JSON.stringify(op)}`);
|
|
}
|
|
|
|
const handler = OP_HANDLERS[op.op];
|
|
if (!handler) {
|
|
throw new Error(
|
|
`editPrefab: 不支持的 op 类型 "${op.op}",支持: ${Object.keys(OP_HANDLERS).join(', ')}`
|
|
);
|
|
}
|
|
|
|
const nodeId = handler(prefabData, op);
|
|
if (typeof nodeId === 'number' && nodeId >= 0) {
|
|
affectedIds.add(nodeId);
|
|
}
|
|
// bulk-set 0 匹配时返回 -1,跳过 affectedIds
|
|
opsApplied++;
|
|
}
|
|
|
|
if (!dryRun) {
|
|
writePrefab(filePath, prefabData.elements, prefabData.raw);
|
|
}
|
|
|
|
for (const id of affectedIds) {
|
|
const node = prefabData.elements[id];
|
|
if (node && node._name) {
|
|
affectedNames.push(node._name);
|
|
} else {
|
|
affectedNames.push(id);
|
|
}
|
|
}
|
|
|
|
const result = {
|
|
changed: !dryRun,
|
|
opsApplied,
|
|
nodesAffected: affectedNames,
|
|
};
|
|
|
|
if (dryRun) {
|
|
result.dryRun = true;
|
|
result.diff = computeDiff(beforeSnapshot, prefabData.elements);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
module.exports = { editPrefab, OP_HANDLERS };
|