cocos2d-cli 1.2.1 → 1.5.0

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.
Files changed (39) hide show
  1. package/README.md +142 -0
  2. package/bin/{cocos-cli.js → cocos2d-cli.js} +36 -22
  3. package/package.json +3 -3
  4. package/src/commands/add-component.js +75 -114
  5. package/src/commands/add.js +115 -294
  6. package/src/commands/build.js +1 -1
  7. package/src/commands/create-scene.js +133 -488
  8. package/src/commands/get.js +104 -158
  9. package/src/commands/prefab-create.js +57 -425
  10. package/src/commands/remove-component.js +81 -33
  11. package/src/commands/remove.js +66 -65
  12. package/src/commands/set.js +105 -253
  13. package/src/commands/tree.js +7 -11
  14. package/src/lib/cc/CCButton.js +122 -0
  15. package/src/lib/cc/CCCamera.js +93 -0
  16. package/src/lib/cc/CCCanvas.js +64 -0
  17. package/src/lib/cc/CCColor.js +32 -0
  18. package/src/lib/cc/CCComponent.js +60 -0
  19. package/src/lib/cc/CCLabel.js +109 -0
  20. package/src/lib/cc/CCNode.js +242 -0
  21. package/src/lib/cc/CCObject.js +23 -0
  22. package/src/lib/cc/CCPrefab.js +242 -0
  23. package/src/lib/cc/CCRect.js +32 -0
  24. package/src/lib/cc/CCScene.js +42 -0
  25. package/src/lib/cc/CCSceneAsset.js +271 -0
  26. package/src/lib/cc/CCSize.js +26 -0
  27. package/src/lib/cc/CCSprite.js +82 -0
  28. package/src/lib/cc/CCTrs.js +74 -0
  29. package/src/lib/cc/CCVec2.js +26 -0
  30. package/src/lib/cc/CCVec3.js +29 -0
  31. package/src/lib/cc/CCWidget.js +98 -0
  32. package/src/lib/cc/index.js +40 -0
  33. package/src/lib/fire-utils.js +58 -392
  34. package/src/lib/json-parser.js +166 -0
  35. package/src/lib/node-utils.js +395 -0
  36. package/src/lib/templates.js +49 -0
  37. package/src/lib/utils.js +202 -0
  38. package/src/commands/delete.js +0 -74
  39. package/src/lib/components.js +0 -404
@@ -1,94 +1,95 @@
1
1
  /**
2
- * remove 命令 - 删除节点或组件
2
+ * remove 命令 - 删除节点
3
3
  */
4
4
 
5
- const { loadScene, saveScene, collectNodeAndChildren, rebuildReferences, refreshEditor, loadScriptMap, buildTree } = require('../lib/fire-utils');
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const { CCSceneAsset, CCPrefab } = require('../lib/cc');
8
+ const { buildTree } = require('../lib/node-utils');
9
+ const { loadScriptMap, isPrefab } = require('../lib/fire-utils');
6
10
 
7
- function run(args) {
8
- if (args.length < 2) {
9
- console.log(JSON.stringify({ error: '用法: cocos2.4 remove <场景文件路径> <索引>' }));
10
- return;
11
+ /**
12
+ * 查找节点
13
+ */
14
+ function findNode(root, path) {
15
+ if (!path) return root;
16
+
17
+ const parts = path.split('/').filter(p => p);
18
+ if (parts.length === 0) return root;
19
+
20
+ let current = root;
21
+
22
+ if (parts[0] === root._name) {
23
+ parts.shift();
11
24
  }
12
25
 
13
- const scenePath = args[0];
26
+ for (const part of parts) {
27
+ if (!current._children || current._children.length === 0) return null;
28
+ const found = current._children.find(c => c._name === part);
29
+ if (!found) return null;
30
+ current = found;
31
+ }
14
32
 
15
- // 索引必须是数字
16
- if (!/^\d+$/.test(args[1])) {
17
- console.log(JSON.stringify({ error: '索引必须是数字,请先用 tree 命令查看节点索引' }));
33
+ return current;
34
+ }
35
+
36
+ function run(args) {
37
+ if (args.length < 2) {
38
+ console.log(JSON.stringify({ error: '用法: cocos2d-cli remove <场景.fire|预制体.prefab> <节点路径>' }));
18
39
  return;
19
40
  }
20
41
 
21
- const index = parseInt(args[1]);
42
+ const filePath = args[0];
43
+ const nodePath = args[1];
44
+
45
+ const ext = path.extname(filePath).toLowerCase();
22
46
 
23
47
  try {
24
- let data = loadScene(scenePath);
25
- const scriptMap = loadScriptMap(scenePath);
48
+ let root;
49
+ let asset;
50
+ const json = JSON.parse(fs.readFileSync(filePath, 'utf8'));
26
51
 
27
- // 检查索引是否存在
28
- if (!data[index]) {
29
- console.log(JSON.stringify({ error: `索引 ${index} 不存在` }));
52
+ if (ext === '.fire') {
53
+ asset = CCSceneAsset.fromJSON(json);
54
+ root = asset._scene;
55
+ } else if (ext === '.prefab') {
56
+ asset = CCPrefab.fromJSON(json);
57
+ root = asset._root;
58
+ } else {
59
+ console.log(JSON.stringify({ error: '不支持的文件类型,仅支持 .fire 和 .prefab' }));
30
60
  return;
31
61
  }
32
62
 
33
- const item = data[index];
34
- const itemType = item.__type__;
63
+ const node = findNode(root, nodePath);
35
64
 
36
- // 判断是节点还是组件
37
- const isNode = itemType === 'cc.Node' || itemType === 'cc.Scene' || item._name !== undefined;
38
- const isComponent = item.node !== undefined;
65
+ if (!node) {
66
+ console.log(JSON.stringify({ error: `节点不存在: ${nodePath}` }));
67
+ return;
68
+ }
39
69
 
40
- if (isNode && index <= 1) {
70
+ // 不能删除根节点
71
+ if (node === root) {
41
72
  console.log(JSON.stringify({ error: '不能删除根节点' }));
42
73
  return;
43
74
  }
44
75
 
45
- if (isComponent) {
46
- // 删除组件
47
- const nodeId = item.node.__id__;
48
- const node = data[nodeId];
49
-
50
- if (node && node._components) {
51
- node._components = node._components.filter(c => c.__id__ !== index);
52
- }
53
-
54
- // 真正从数组中删除组件并重建引用
55
- const indicesToDelete = new Set([index]);
56
- rebuildReferences(data, indicesToDelete);
57
- data.splice(index, 1);
58
-
59
- } else {
60
- // 删除节点
61
- // 收集所有需要删除的索引
62
- const indicesToDelete = collectNodeAndChildren(data, index);
63
-
64
- // 从父节点的 _children 中移除引用
65
- if (item._parent) {
66
- const parentIndex = item._parent.__id__;
67
- const parent = data[parentIndex];
68
- if (parent && parent._children) {
69
- parent._children = parent._children.filter(c => c.__id__ !== index);
70
- }
71
- }
72
-
73
- // 重建引用
74
- rebuildReferences(data, indicesToDelete);
75
-
76
- // 删除元素
77
- const sortedIndices = Array.from(indicesToDelete).sort((a, b) => b - a);
78
- for (const idx of sortedIndices) {
79
- data.splice(idx, 1);
76
+ // 从父节点移除
77
+ if (node._parent) {
78
+ const idx = node._parent._children.indexOf(node);
79
+ if (idx > -1) {
80
+ node._parent._children.splice(idx, 1);
80
81
  }
81
82
  }
82
83
 
83
- // 保存场景
84
- saveScene(scenePath, data);
85
-
86
- // 触发编辑器刷新
87
- refreshEditor(scenePath);
84
+ // 保存
85
+ const data = asset.toJSON();
86
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
88
87
 
89
- // 重新加载并显示最新树
90
- data = loadScene(scenePath);
91
- console.log(buildTree(data, scriptMap, 1));
88
+ // 输出节点树
89
+ const scriptMap = loadScriptMap(filePath);
90
+ const prefab = isPrefab(data);
91
+ const startIndex = prefab ? 0 : 1;
92
+ console.log(buildTree(data, scriptMap, startIndex).trim());
92
93
 
93
94
  } catch (err) {
94
95
  console.log(JSON.stringify({ error: err.message }));
@@ -1,296 +1,148 @@
1
1
  /**
2
- * set 命令 - 修改节点属性
3
- * 修改成功后返回节点最终状态(与 get 命令格式一致)
2
+ * set 命令 - 设置节点或组件属性
4
3
  */
5
4
 
6
- const { loadScene, saveScene, buildMaps, findNodeIndex, refreshEditor } = require('../lib/fire-utils');
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const { CCSceneAsset, CCPrefab } = require('../lib/cc');
8
+ const { buildTree } = require('../lib/node-utils');
9
+ const { loadScriptMap, isPrefab } = require('../lib/fire-utils');
7
10
 
8
11
  /**
9
- * 将 _color 对象转为 #RRGGBB 字符串
12
+ * 查找节点
10
13
  */
11
- function colorToHex(color) {
12
- if (!color) return '#ffffff';
13
- const r = (color.r || 0).toString(16).padStart(2, '0');
14
- const g = (color.g || 0).toString(16).padStart(2, '0');
15
- const b = (color.b || 0).toString(16).padStart(2, '0');
16
- return `#${r}${g}${b}`;
14
+ function findNode(root, path) {
15
+ if (!path) return root;
16
+
17
+ const parts = path.split('/').filter(p => p);
18
+ if (parts.length === 0) return root;
19
+
20
+ let current = root;
21
+
22
+ if (parts[0] === root._name) {
23
+ parts.shift();
24
+ }
25
+
26
+ for (const part of parts) {
27
+ if (!current._children || current._children.length === 0) return null;
28
+ const found = current._children.find(c => c._name === part);
29
+ if (!found) return null;
30
+ current = found;
31
+ }
32
+
33
+ return current;
17
34
  }
18
35
 
19
36
  /**
20
- * 提取组件的关键属性(对应 Inspector 面板显示)
37
+ * 查找组件
21
38
  */
22
- function extractComponentProps(comp) {
23
- if (!comp) return null;
24
- const type = comp.__type__;
25
- const base = comp._enabled ? { type } : { type, enabled: false };
26
-
27
- const clean = (obj) => {
28
- if (!obj || typeof obj !== 'object') return obj;
29
- if (Array.isArray(obj)) return obj.map(clean);
30
- const result = {};
31
- for (const [k, v] of Object.entries(obj)) {
32
- if (k === '__type__') continue;
33
- result[k] = typeof v === 'object' ? clean(v) : v;
34
- }
35
- return result;
36
- };
37
-
38
- switch (type) {
39
- case 'cc.Sprite':
40
- return {
41
- ...base,
42
- spriteFrame: comp._spriteFrame?.__uuid__ || null,
43
- sizeMode: ['CUSTOM', 'TRIMMED', 'RAW'][comp._sizeMode] || comp._sizeMode,
44
- spriteType: ['SIMPLE', 'SLICED', 'TILED', 'FILLED', 'MESH'][comp._type] || comp._type,
45
- trim: comp._isTrimmedMode
46
- };
47
- case 'cc.Label':
48
- return {
49
- ...base,
50
- string: comp._string,
51
- fontSize: comp._fontSize,
52
- lineHeight: comp._lineHeight,
53
- horizontalAlign: ['LEFT', 'CENTER', 'RIGHT'][comp._N$horizontalAlign] || comp._N$horizontalAlign,
54
- verticalAlign: ['TOP', 'CENTER', 'BOTTOM'][comp._N$verticalAlign] || comp._N$verticalAlign,
55
- overflow: ['NONE', 'CLAMP', 'SHRINK', 'RESIZE_HEIGHT'][comp._N$overflow] || comp._N$overflow,
56
- fontFamily: comp._N$fontFamily,
57
- enableWrapText: comp._enableWrapText
58
- };
59
- case 'cc.Button':
60
- return {
61
- ...base,
62
- interactable: comp._N$interactable,
63
- transition: ['NONE', 'COLOR', 'SPRITE', 'SCALE'][comp._N$transition] || comp._N$transition,
64
- zoomScale: comp.zoomScale,
65
- duration: comp.duration
66
- };
67
- case 'cc.Widget':
68
- return {
69
- ...base,
70
- alignMode: ['ONCE', 'ON_WINDOW_RESIZE', 'ALWAYS'][comp.alignMode] || comp.alignMode,
71
- left: comp._left,
72
- right: comp._right,
73
- top: comp._top,
74
- bottom: comp._bottom
75
- };
76
- case 'cc.Layout':
77
- return {
78
- ...base,
79
- layoutType: ['NONE', 'HORIZONTAL', 'VERTICAL', 'GRID'][comp._N$layoutType] || comp._N$layoutType,
80
- spacingX: comp._N$spacingX,
81
- spacingY: comp._N$spacingY,
82
- paddingLeft: comp._N$paddingLeft,
83
- paddingRight: comp._N$paddingRight,
84
- paddingTop: comp._N$paddingTop,
85
- paddingBottom: comp._N$paddingBottom
86
- };
87
- case 'cc.Canvas':
88
- return {
89
- ...base,
90
- designResolution: clean(comp._designResolution),
91
- fitWidth: comp._fitWidth,
92
- fitHeight: comp._fitHeight
93
- };
94
- case 'cc.Camera':
95
- return {
96
- ...base,
97
- depth: comp._depth,
98
- zoomRatio: comp._zoomRatio,
99
- ortho: comp._ortho,
100
- cullingMask: comp._cullingMask
101
- };
102
- case 'cc.ParticleSystem':
103
- return {
104
- ...base,
105
- playOnLoad: comp.playOnLoad,
106
- totalParticles: comp.totalParticles,
107
- duration: comp.duration
108
- };
109
- default:
110
- const result = { ...base };
111
- for (const key of Object.keys(comp)) {
112
- if (!key.startsWith('_') && key !== '__type__') {
113
- result[key] = comp[key];
114
- }
115
- }
116
- return result;
117
- }
118
- }
119
-
120
- // 解析颜色
121
- function parseColor(colorStr) {
122
- if (!colorStr) return null;
123
- let color = colorStr;
124
- if (typeof color === 'string') {
125
- if (color.startsWith('#')) color = color.slice(1);
126
- if (color.length === 6) {
127
- const r = parseInt(color.slice(0, 2), 16);
128
- const g = parseInt(color.slice(2, 4), 16);
129
- const b = parseInt(color.slice(4, 6), 16);
130
- if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
131
- return { "__type__": "cc.Color", r, g, b, a: 255 };
132
- }
133
- }
134
- }
135
- return null;
39
+ function findComponent(node, compType) {
40
+ if (!node._components) return null;
41
+
42
+ const type = compType.toLowerCase();
43
+ const typeName = 'cc.' + type.charAt(0).toUpperCase() + type.slice(1);
44
+
45
+ return node._components.find(c => c.__type__ === typeName);
136
46
  }
137
47
 
138
48
  /**
139
- * 获取节点的完整状态(与 get 命令一致)
49
+ * 设置节点属性值
140
50
  */
141
- function getNodeState(data, node, nodeIndex) {
142
- const trs = node._trs?.array || [0,0,0, 0,0,0,1, 1,1,1];
143
- const components = (node._components || []).map(ref => extractComponentProps(data[ref.__id__]));
144
- const children = (node._children || []).map(ref => data[ref.__id__]?._name || '(unknown)');
145
-
146
- const result = {
147
- name: node._name,
148
- active: node._active,
149
- position: { x: trs[0], y: trs[1] },
150
- rotation: node._eulerAngles?.z ?? 0,
151
- scale: { x: trs[7], y: trs[8] },
152
- anchor: { x: node._anchorPoint?.x ?? 0.5, y: node._anchorPoint?.y ?? 0.5 },
153
- size: { w: node._contentSize?.width ?? 0, h: node._contentSize?.height ?? 0 },
154
- color: colorToHex(node._color),
155
- opacity: node._opacity ?? 255,
156
- group: node._groupIndex ?? 0
157
- };
158
-
159
- if (children.length > 0) result.children = children;
160
- if (components.length > 0) result.components = components;
161
-
162
- return result;
51
+ function setNodeProp(node, prop, value) {
52
+ switch (prop.toLowerCase()) {
53
+ case 'name': node._name = value; return true;
54
+ case 'active': node._active = value === 'true' || value === true; return true;
55
+ case 'x': node._trs.array[0] = parseFloat(value); return true;
56
+ case 'y': node._trs.array[1] = parseFloat(value); return true;
57
+ case 'width': node._contentSize.width = parseFloat(value); return true;
58
+ case 'height': node._contentSize.height = parseFloat(value); return true;
59
+ case 'scalex': node._trs.array[7] = parseFloat(value); return true;
60
+ case 'scaley': node._trs.array[8] = parseFloat(value); return true;
61
+ case 'rotation':
62
+ node._trs.array[5] = parseFloat(value) * Math.PI / 180;
63
+ node._eulerAngles.z = parseFloat(value);
64
+ return true;
65
+ case 'anchorx': node._anchorPoint.x = parseFloat(value); return true;
66
+ case 'anchory': node._anchorPoint.y = parseFloat(value); return true;
67
+ case 'opacity': node._opacity = parseInt(value); return true;
68
+ default: return false;
69
+ }
163
70
  }
164
71
 
165
72
  function run(args) {
166
- if (args.length < 2) {
167
- console.log(JSON.stringify({ error: '用法: cocos2.4 set <场景.fire | 预制体.prefab> <节点索引|名称> [选项]' }));
73
+ if (args.length < 4) {
74
+ console.log(JSON.stringify({ error: '用法: cocos2d-cli set <场景.fire|预制体.prefab> <节点路径> <属性名|组件.属性> <值>' }));
168
75
  return;
169
76
  }
170
77
 
171
- const scenePath = args[0];
172
- const nodeRef = args[1];
78
+ const filePath = args[0];
79
+ const nodePath = args[1];
80
+ const propPath = args[2];
81
+ const value = args[3];
173
82
 
174
- // 解析选项
175
- const options = {};
176
- args.slice(2).forEach(arg => {
177
- if (arg.startsWith('--')) {
178
- const [key, value] = arg.substring(2).split('=');
179
- options[key] = value;
180
- }
181
- });
83
+ const ext = path.extname(filePath).toLowerCase();
182
84
 
183
85
  try {
184
- const data = loadScene(scenePath);
185
- const { indexMap } = buildMaps(data);
186
-
187
- // 查找节点
188
- const nodeIndex = findNodeIndex(data, indexMap, nodeRef);
189
-
190
- if (nodeIndex === null || !data[nodeIndex]) {
191
- console.log(JSON.stringify({ error: `找不到节点: ${nodeRef}` }));
86
+ let root;
87
+ let asset;
88
+ const json = JSON.parse(fs.readFileSync(filePath, 'utf8'));
89
+
90
+ if (ext === '.fire') {
91
+ asset = CCSceneAsset.fromJSON(json);
92
+ root = asset._scene;
93
+ } else if (ext === '.prefab') {
94
+ asset = CCPrefab.fromJSON(json);
95
+ root = asset._root;
96
+ } else {
97
+ console.log(JSON.stringify({ error: '不支持的文件类型,仅支持 .fire 和 .prefab' }));
192
98
  return;
193
99
  }
194
100
 
195
- const node = data[nodeIndex];
101
+ const node = findNode(root, nodePath);
196
102
 
197
- // 修改名称
198
- if (options.name !== undefined) {
199
- node._name = options.name;
200
- }
201
-
202
- // 修改激活状态
203
- if (options.active !== undefined) {
204
- node._active = options.active !== 'false';
205
- }
206
-
207
- // 修改位置
208
- if (options.x !== undefined || options.y !== undefined) {
209
- if (!node._trs) {
210
- node._trs = { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] };
211
- }
212
- if (options.x !== undefined) node._trs.array[0] = parseFloat(options.x);
213
- if (options.y !== undefined) node._trs.array[1] = parseFloat(options.y);
214
- }
215
-
216
- // 修改大小
217
- if (options.width !== undefined || options.height !== undefined) {
218
- if (!node._contentSize) {
219
- node._contentSize = { "__type__": "cc.Size", width: 0, height: 0 };
220
- }
221
- if (options.width !== undefined) node._contentSize.width = parseFloat(options.width);
222
- if (options.height !== undefined) node._contentSize.height = parseFloat(options.height);
223
- }
224
-
225
- // 修改锚点
226
- if (options.anchorX !== undefined || options.anchorY !== undefined) {
227
- if (!node._anchorPoint) {
228
- node._anchorPoint = { "__type__": "cc.Vec2", x: 0.5, y: 0.5 };
229
- }
230
- if (options.anchorX !== undefined) node._anchorPoint.x = parseFloat(options.anchorX);
231
- if (options.anchorY !== undefined) node._anchorPoint.y = parseFloat(options.anchorY);
232
- }
233
-
234
- // 修改透明度
235
- if (options.opacity !== undefined) {
236
- node._opacity = Math.max(0, Math.min(255, parseInt(options.opacity)));
237
- }
238
-
239
- // 修改颜色
240
- if (options.color !== undefined) {
241
- const color = parseColor(options.color);
242
- if (color) {
243
- node._color = color;
244
- }
245
- }
246
-
247
- // 修改旋转角度
248
- if (options.rotation !== undefined) {
249
- if (!node._eulerAngles) {
250
- node._eulerAngles = { "__type__": "cc.Vec3", x: 0, y: 0, z: 0 };
251
- }
252
- node._eulerAngles.z = parseFloat(options.rotation);
103
+ if (!node) {
104
+ console.log(JSON.stringify({ error: `节点不存在: ${nodePath}` }));
105
+ return;
253
106
  }
254
107
 
255
- // 修改缩放
256
- if (options.scaleX !== undefined || options.scaleY !== undefined) {
257
- if (!node._trs) {
258
- node._trs = { "__type__": "TypedArray", "ctor": "Float64Array", "array": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] };
259
- }
260
- if (options.scaleX !== undefined) node._trs.array[7] = parseFloat(options.scaleX);
261
- if (options.scaleY !== undefined) node._trs.array[8] = parseFloat(options.scaleY);
262
- }
108
+ // 检查是否是组件属性(格式: 组件.属性)
109
+ const parts = propPath.split('.');
263
110
 
264
- // 修改 Label 文字内容及字体属性
265
- if (options.string !== undefined || options.fontSize !== undefined || options.lineHeight !== undefined) {
266
- const labelComp = (node._components || []).map(ref => data[ref.__id__]).find(c => c && c.__type__ === 'cc.Label');
267
- if (!labelComp) {
268
- console.log(JSON.stringify({ error: `节点 ${node._name} 没有 cc.Label 组件,无法设置文字属性` }));
111
+ if (parts.length === 2) {
112
+ // 组件属性
113
+ const comp = findComponent(node, parts[0]);
114
+ if (!comp) {
115
+ console.log(JSON.stringify({ error: `组件不存在: ${parts[0]}` }));
269
116
  return;
270
117
  }
271
- if (options.string !== undefined) {
272
- labelComp._string = options.string;
273
- labelComp._N$string = options.string;
274
- }
275
- if (options.fontSize !== undefined) {
276
- labelComp._fontSize = parseInt(options.fontSize);
118
+
119
+ if (comp[parts[1]] === undefined) {
120
+ console.log(JSON.stringify({ error: `属性不存在: ${parts[1]}` }));
121
+ return;
277
122
  }
278
- if (options.lineHeight !== undefined) {
279
- labelComp._lineHeight = parseInt(options.lineHeight);
123
+
124
+ comp[parts[1]] = value;
125
+ } else {
126
+ // 节点属性
127
+ if (!setNodeProp(node, propPath, value)) {
128
+ console.log(JSON.stringify({ error: `属性不存在: ${propPath}` }));
129
+ return;
280
130
  }
281
131
  }
282
132
 
283
- // 保存场景
284
- saveScene(scenePath, data);
133
+ // 保存
134
+ const data = asset.toJSON();
135
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
285
136
 
286
- // 触发编辑器刷新
287
- refreshEditor(scenePath);
137
+ // 输出节点树
138
+ const scriptMap = loadScriptMap(filePath);
139
+ const prefab = isPrefab(data);
140
+ const startIndex = prefab ? 0 : 1;
141
+ console.log(buildTree(data, scriptMap, startIndex).trim());
288
142
 
289
- // 返回节点最终状态(与 get 命令格式一致)
290
- console.log(JSON.stringify(getNodeState(data, node, nodeIndex)));
291
143
  } catch (err) {
292
144
  console.log(JSON.stringify({ error: err.message }));
293
145
  }
294
146
  }
295
147
 
296
- module.exports = { run };
148
+ module.exports = { run };
@@ -2,13 +2,14 @@
2
2
  * tree 命令 - 查看节点树(支持场景和预制体)
3
3
  */
4
4
 
5
- const { loadScene, loadScriptMap, buildTree, isPrefab } = require('../lib/fire-utils');
5
+ const { loadScene, loadScriptMap, isPrefab } = require('../lib/fire-utils');
6
+ const { buildTree } = require('../lib/node-utils');
6
7
 
7
8
  function run(args) {
8
9
  const filePath = args[0];
9
10
 
10
11
  if (!filePath) {
11
- console.log(JSON.stringify({ error: '用法: cocos2.4 tree <场景.fire | 预制体.prefab>' }));
12
+ console.log(JSON.stringify({ error: '用法: cocos2d-cli tree <场景.fire | 预制体.prefab>' }));
12
13
  return;
13
14
  }
14
15
 
@@ -17,17 +18,12 @@ function run(args) {
17
18
  const scriptMap = loadScriptMap(filePath);
18
19
  const prefab = isPrefab(data);
19
20
 
20
- // 输出文件类型
21
- if (prefab) {
22
- console.log(`[Prefab] ${data[1]._name || 'Root'}\n`);
23
- } else {
24
- console.log(`[Scene]\n`);
25
- }
26
-
27
- console.log(buildTree(data, scriptMap, 1));
21
+ // Prefab 从索引 0 开始,Scene 从索引 1 开始
22
+ const startIndex = prefab ? 0 : 1;
23
+ console.log(buildTree(data, scriptMap, startIndex).trim());
28
24
  } catch (err) {
29
25
  console.log(JSON.stringify({ error: err.message }));
30
26
  }
31
27
  }
32
28
 
33
- module.exports = { run };
29
+ module.exports = { run };