cocos2d-cli 1.0.3
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/bin/cocos-cli.js +93 -0
- package/data/script_map.json +25 -0
- package/package.json +31 -0
- package/src/commands/add-component.js +151 -0
- package/src/commands/add.js +227 -0
- package/src/commands/build.js +78 -0
- package/src/commands/delete.js +73 -0
- package/src/commands/get.js +53 -0
- package/src/commands/remove-component.js +63 -0
- package/src/commands/remove.js +163 -0
- package/src/commands/set.js +163 -0
- package/src/commands/tree.js +92 -0
- package/src/lib/components.js +404 -0
- package/src/lib/fire-utils.js +412 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire 文件工具模块
|
|
3
|
+
* 提供直接读取和操作 .fire 场景文件的功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 加载场景文件
|
|
11
|
+
*/
|
|
12
|
+
function loadScene(scenePath) {
|
|
13
|
+
if (!fs.existsSync(scenePath)) {
|
|
14
|
+
throw new Error(`场景文件不存在: ${scenePath}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const content = fs.readFileSync(scenePath, 'utf8');
|
|
18
|
+
return JSON.parse(content);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 保存场景文件
|
|
23
|
+
*/
|
|
24
|
+
function saveScene(scenePath, data) {
|
|
25
|
+
fs.writeFileSync(scenePath, JSON.stringify(data, null, 2), 'utf8');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 构建 ID 和索引映射
|
|
30
|
+
*/
|
|
31
|
+
function buildMaps(data) {
|
|
32
|
+
const idMap = {}; // _id -> index
|
|
33
|
+
const indexMap = {}; // index -> { _id, name, path }
|
|
34
|
+
|
|
35
|
+
function traverse(nodeIndex, parentPath = '') {
|
|
36
|
+
const node = data[nodeIndex];
|
|
37
|
+
if (!node) return;
|
|
38
|
+
|
|
39
|
+
const nodeId = node._id;
|
|
40
|
+
if (nodeId) {
|
|
41
|
+
idMap[nodeId] = nodeIndex;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const nodeName = node._name || '(unnamed)';
|
|
45
|
+
const nodePath = parentPath ? `${parentPath}/${nodeName}` : nodeName;
|
|
46
|
+
|
|
47
|
+
indexMap[nodeIndex] = {
|
|
48
|
+
_id: nodeId,
|
|
49
|
+
name: nodeName,
|
|
50
|
+
path: nodePath,
|
|
51
|
+
type: node.__type__
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// 递归处理子节点
|
|
55
|
+
if (node._children) {
|
|
56
|
+
node._children.forEach(childRef => {
|
|
57
|
+
traverse(childRef.__id__, nodePath);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 从 Scene 开始遍历(索引 1)
|
|
63
|
+
if (data[1]) {
|
|
64
|
+
traverse(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { idMap, indexMap };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 查找节点索引
|
|
72
|
+
*/
|
|
73
|
+
function findNodeIndex(data, indexMap, nodeRef) {
|
|
74
|
+
// 如果是数字,直接返回
|
|
75
|
+
if (/^\d+$/.test(nodeRef)) {
|
|
76
|
+
return parseInt(nodeRef);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 按名称/路径查找
|
|
80
|
+
for (const [idx, info] of Object.entries(indexMap)) {
|
|
81
|
+
if (info.name === nodeRef || info.path === nodeRef || info.path.endsWith('/' + nodeRef)) {
|
|
82
|
+
return parseInt(idx);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 递归收集节点及其所有子节点和组件的索引
|
|
91
|
+
*/
|
|
92
|
+
function collectNodeAndChildren(data, nodeIndex, collected = new Set()) {
|
|
93
|
+
if (collected.has(nodeIndex)) return collected;
|
|
94
|
+
|
|
95
|
+
const node = data[nodeIndex];
|
|
96
|
+
if (!node) return collected;
|
|
97
|
+
|
|
98
|
+
collected.add(nodeIndex);
|
|
99
|
+
|
|
100
|
+
// 收集所有组件
|
|
101
|
+
if (node._components) {
|
|
102
|
+
for (const compRef of node._components) {
|
|
103
|
+
collected.add(compRef.__id__);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 递归收集子节点
|
|
108
|
+
if (node._children) {
|
|
109
|
+
for (const childRef of node._children) {
|
|
110
|
+
collectNodeAndChildren(data, childRef.__id__, collected);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return collected;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 重建所有 __id__ 引用(删除元素后索引变化)
|
|
119
|
+
*/
|
|
120
|
+
function rebuildReferences(data, deletedIndices) {
|
|
121
|
+
const indexMap = {};
|
|
122
|
+
let newIndex = 0;
|
|
123
|
+
for (let oldIndex = 0; oldIndex < data.length; oldIndex++) {
|
|
124
|
+
if (!deletedIndices.has(oldIndex)) {
|
|
125
|
+
indexMap[oldIndex] = newIndex;
|
|
126
|
+
newIndex++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function updateRef(obj) {
|
|
131
|
+
if (!obj || typeof obj !== 'object') return;
|
|
132
|
+
|
|
133
|
+
if (obj.__id__ !== undefined) {
|
|
134
|
+
const oldId = obj.__id__;
|
|
135
|
+
if (indexMap[oldId] !== undefined) {
|
|
136
|
+
obj.__id__ = indexMap[oldId];
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
for (const key of Object.keys(obj)) {
|
|
140
|
+
updateRef(obj[key]);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (const item of data) {
|
|
146
|
+
updateRef(item);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return indexMap;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 重新排列数组,使其与 _children 顺序一致(节点后跟组件)
|
|
154
|
+
*/
|
|
155
|
+
function reorderArrayToMatchChildren(data) {
|
|
156
|
+
const newArray = [];
|
|
157
|
+
const indexMap = {};
|
|
158
|
+
|
|
159
|
+
newArray[0] = data[0];
|
|
160
|
+
newArray[1] = data[1];
|
|
161
|
+
indexMap[0] = 0;
|
|
162
|
+
indexMap[1] = 1;
|
|
163
|
+
|
|
164
|
+
const dataByIndex = {};
|
|
165
|
+
for (let i = 0; i < data.length; i++) {
|
|
166
|
+
if (data[i]) dataByIndex[i] = data[i];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function addNodeAndChildren(nodeIndex) {
|
|
170
|
+
if (nodeIndex === null || nodeIndex === undefined) return;
|
|
171
|
+
|
|
172
|
+
const node = data[nodeIndex];
|
|
173
|
+
if (!node) return;
|
|
174
|
+
|
|
175
|
+
const newIndex = newArray.length;
|
|
176
|
+
indexMap[nodeIndex] = newIndex;
|
|
177
|
+
newArray.push(node);
|
|
178
|
+
|
|
179
|
+
if (node._components) {
|
|
180
|
+
for (const compRef of node._components) {
|
|
181
|
+
const compIndex = compRef.__id__;
|
|
182
|
+
if (compIndex !== undefined && dataByIndex[compIndex]) {
|
|
183
|
+
const compNewIndex = newArray.length;
|
|
184
|
+
indexMap[compIndex] = compNewIndex;
|
|
185
|
+
newArray.push(dataByIndex[compIndex]);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (node._children) {
|
|
191
|
+
for (const childRef of node._children) {
|
|
192
|
+
addNodeAndChildren(childRef.__id__);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const scene = data[1];
|
|
198
|
+
if (scene && scene._children) {
|
|
199
|
+
for (const childRef of scene._children) {
|
|
200
|
+
addNodeAndChildren(childRef.__id__);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function addRootComponents(nodeIndex) {
|
|
205
|
+
const node = data[nodeIndex];
|
|
206
|
+
if (!node || !node._components) return;
|
|
207
|
+
|
|
208
|
+
for (const compRef of node._components) {
|
|
209
|
+
const compIndex = compRef.__id__;
|
|
210
|
+
if (compIndex !== undefined && dataByIndex[compIndex] && indexMap[compIndex] === undefined) {
|
|
211
|
+
const compNewIndex = newArray.length;
|
|
212
|
+
indexMap[compIndex] = compNewIndex;
|
|
213
|
+
newArray.push(dataByIndex[compIndex]);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
addRootComponents(1);
|
|
219
|
+
addRootComponents(2);
|
|
220
|
+
|
|
221
|
+
function updateRefs(obj) {
|
|
222
|
+
if (!obj || typeof obj !== 'object') return;
|
|
223
|
+
|
|
224
|
+
if (obj.__id__ !== undefined) {
|
|
225
|
+
const oldId = obj.__id__;
|
|
226
|
+
if (indexMap[oldId] !== undefined) {
|
|
227
|
+
obj.__id__ = indexMap[oldId];
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
for (const key of Object.keys(obj)) {
|
|
231
|
+
updateRefs(obj[key]);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (const item of newArray) {
|
|
237
|
+
updateRefs(item);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return newArray;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* 检查 CLI Helper 插件状态
|
|
245
|
+
* @returns {object|null} - 插件状态信息,未启动返回 null
|
|
246
|
+
*/
|
|
247
|
+
function checkPluginStatus() {
|
|
248
|
+
const { execSync } = require('child_process');
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const result = execSync('curl.exe -s http://localhost:7455/status', {
|
|
252
|
+
timeout: 3000,
|
|
253
|
+
windowsHide: true,
|
|
254
|
+
encoding: 'utf8'
|
|
255
|
+
});
|
|
256
|
+
return JSON.parse(result);
|
|
257
|
+
} catch (e) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 触发 Cocos Creator 编辑器刷新资源
|
|
264
|
+
* 智能判断:如果修改的场景就是当前打开的场景,才重新打开;否则只刷新资源
|
|
265
|
+
* 编辑器有可能没打开,调用失败不报错
|
|
266
|
+
* @param {string} scenePath - 场景文件路径(必须)
|
|
267
|
+
*/
|
|
268
|
+
function refreshEditor(scenePath) {
|
|
269
|
+
// 参数校验
|
|
270
|
+
if (!scenePath) {
|
|
271
|
+
console.log(JSON.stringify({ warning: 'refreshEditor: 未提供 scenePath 参数,编辑器不会自动刷新场景' }));
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const path = require('path');
|
|
276
|
+
const http = require('http');
|
|
277
|
+
|
|
278
|
+
// 转换场景路径为 db:// 格式
|
|
279
|
+
const assetsPath = path.dirname(scenePath);
|
|
280
|
+
const projectPath = path.dirname(assetsPath);
|
|
281
|
+
const relativePath = path.relative(projectPath, scenePath).replace(/\\/g, '/');
|
|
282
|
+
const targetSceneUrl = 'db://' + relativePath.replace(/^assets\//, 'assets/');
|
|
283
|
+
|
|
284
|
+
// 先查询当前打开的场景
|
|
285
|
+
const getCurrentScene = () => {
|
|
286
|
+
return new Promise((resolve) => {
|
|
287
|
+
const options = {
|
|
288
|
+
hostname: 'localhost',
|
|
289
|
+
port: 7455,
|
|
290
|
+
path: '/current-scene',
|
|
291
|
+
method: 'GET'
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const req = http.request(options, (res) => {
|
|
295
|
+
let data = '';
|
|
296
|
+
res.on('data', chunk => data += chunk);
|
|
297
|
+
res.on('end', () => {
|
|
298
|
+
try {
|
|
299
|
+
const result = JSON.parse(data);
|
|
300
|
+
resolve(result.sceneUrl || null);
|
|
301
|
+
} catch (e) {
|
|
302
|
+
resolve(null);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
req.on('error', () => resolve(null));
|
|
308
|
+
req.setTimeout(3000, () => {
|
|
309
|
+
req.destroy();
|
|
310
|
+
resolve(null);
|
|
311
|
+
});
|
|
312
|
+
req.end();
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// 发送刷新请求
|
|
317
|
+
const sendRefreshRequest = (sceneUrl) => {
|
|
318
|
+
const postData = sceneUrl ? JSON.stringify({ sceneUrl }) : '';
|
|
319
|
+
|
|
320
|
+
const options = {
|
|
321
|
+
hostname: 'localhost',
|
|
322
|
+
port: 7455,
|
|
323
|
+
path: '/refresh',
|
|
324
|
+
method: 'POST',
|
|
325
|
+
headers: {
|
|
326
|
+
'Content-Type': 'application/json',
|
|
327
|
+
'Content-Length': Buffer.byteLength(postData)
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const req = http.request(options, (res) => {
|
|
332
|
+
// 忽略响应
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
req.on('error', () => {
|
|
336
|
+
// 插件未启动,静默处理
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (postData) {
|
|
340
|
+
req.write(postData);
|
|
341
|
+
}
|
|
342
|
+
req.end();
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// 执行刷新逻辑
|
|
346
|
+
getCurrentScene().then(currentSceneUrl => {
|
|
347
|
+
if (currentSceneUrl && currentSceneUrl === targetSceneUrl) {
|
|
348
|
+
// 是当前打开的场景,重新打开
|
|
349
|
+
sendRefreshRequest(targetSceneUrl);
|
|
350
|
+
} else {
|
|
351
|
+
// 不是当前场景,只刷新资源
|
|
352
|
+
sendRefreshRequest(null);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* 检测并安装 CLI Helper 插件到项目
|
|
359
|
+
* @param {string} scenePath - 场景文件路径
|
|
360
|
+
* @returns {boolean} - 是否已安装
|
|
361
|
+
*/
|
|
362
|
+
function installPlugin(scenePath) {
|
|
363
|
+
try {
|
|
364
|
+
const fs = require('fs');
|
|
365
|
+
const path = require('path');
|
|
366
|
+
|
|
367
|
+
// 获取项目路径
|
|
368
|
+
const assetsPath = path.dirname(scenePath);
|
|
369
|
+
const projectPath = path.dirname(assetsPath);
|
|
370
|
+
const packagesPath = path.join(projectPath, 'packages');
|
|
371
|
+
const pluginPath = path.join(packagesPath, 'cocos-cli-helper');
|
|
372
|
+
|
|
373
|
+
// 如果插件已存在,直接返回
|
|
374
|
+
if (fs.existsSync(pluginPath)) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 创建 packages 目录
|
|
379
|
+
if (!fs.existsSync(packagesPath)) {
|
|
380
|
+
fs.mkdirSync(packagesPath, { recursive: true });
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// 获取 CLI 自带的插件路径
|
|
384
|
+
const cliPluginPath = path.join(__dirname, '..', '..', 'editor-plugin', 'cocos-cli-helper');
|
|
385
|
+
|
|
386
|
+
if (!fs.existsSync(cliPluginPath)) {
|
|
387
|
+
console.log('[CLI] 插件源文件不存在');
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// 复制插件
|
|
392
|
+
fs.cpSync(cliPluginPath, pluginPath, { recursive: true });
|
|
393
|
+
console.log('[CLI] CLI Helper 插件已安装到项目,请在编辑器中启用');
|
|
394
|
+
return true;
|
|
395
|
+
} catch (e) {
|
|
396
|
+
console.log(`[CLI] 安装插件失败: ${e.message}`);
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
module.exports = {
|
|
402
|
+
loadScene,
|
|
403
|
+
saveScene,
|
|
404
|
+
buildMaps,
|
|
405
|
+
findNodeIndex,
|
|
406
|
+
collectNodeAndChildren,
|
|
407
|
+
rebuildReferences,
|
|
408
|
+
reorderArrayToMatchChildren,
|
|
409
|
+
refreshEditor,
|
|
410
|
+
installPlugin,
|
|
411
|
+
checkPluginStatus
|
|
412
|
+
};
|