cocos2d-cli 1.6.2 → 1.6.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/README.md +429 -80
- package/package.json +1 -1
- package/src/commands/screenshot.js +6 -3
- package/src/lib/screenshot-core.js +73 -52
package/README.md
CHANGED
|
@@ -1,142 +1,491 @@
|
|
|
1
|
-
# cocos2d-cli
|
|
1
|
+
# cocos2d-cli
|
|
2
2
|
|
|
3
|
-
Cocos Creator
|
|
3
|
+
Cocos Creator 2.4.x 场景 / 预制体命令行工具。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
它可以直接操作 `.fire` 和 `.prefab` 文件,也支持从简化 JSON 生成场景、预制体,以及通过截图方式快速预览 UI 结构。
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
7
|
+
适用场景:
|
|
8
|
+
|
|
9
|
+
- 批量修改 Cocos Creator 资源
|
|
10
|
+
- 通过脚本或 AI 自动生成 UI
|
|
11
|
+
- 在不打开编辑器的情况下预览 JSON 描述的界面
|
|
12
|
+
|
|
13
|
+
## 特性
|
|
14
|
+
|
|
15
|
+
- 查看场景 / 预制体节点树
|
|
16
|
+
- 查询节点属性和组件属性
|
|
17
|
+
- 修改节点属性
|
|
18
|
+
- 修改组件属性
|
|
19
|
+
- 添加 / 删除节点
|
|
20
|
+
- 添加 / 删除组件
|
|
21
|
+
- 从 JSON 创建预制体
|
|
22
|
+
- 从 JSON 创建场景
|
|
23
|
+
- 构建脚本组件映射
|
|
24
|
+
- 将 JSON 渲染为截图
|
|
25
|
+
|
|
26
|
+
## 环境要求
|
|
27
|
+
|
|
28
|
+
- Node.js >= 14
|
|
29
|
+
- Cocos Creator 2.4.x
|
|
30
|
+
|
|
31
|
+
> `screenshot` 命令依赖 `playwright`。
|
|
12
32
|
|
|
13
33
|
## 安装
|
|
14
34
|
|
|
35
|
+
全局安装:
|
|
36
|
+
|
|
15
37
|
```bash
|
|
16
38
|
npm install -g cocos2d-cli
|
|
17
39
|
```
|
|
18
40
|
|
|
19
|
-
|
|
41
|
+
或在项目中直接使用:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx cocos2d-cli --help
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 快速开始
|
|
48
|
+
|
|
49
|
+
查看帮助:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cocos2d-cli --help
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
查看节点树:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cocos2d-cli tree assets/main.fire
|
|
59
|
+
cocos2d-cli tree assets/panel.prefab
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
从 JSON 预览并生成预制体:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cocos2d-cli screenshot panel.json -o ./screenshots --width 750 --height 1334
|
|
66
|
+
cocos2d-cli create-prefab panel.json assets/Panel.prefab
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 命令说明
|
|
70
|
+
|
|
71
|
+
### tree
|
|
72
|
+
|
|
73
|
+
查看场景或预制体的节点树。
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cocos2d-cli tree <scene.fire | prefab.prefab>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
示例:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
cocos2d-cli tree assets/main.fire
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### get
|
|
86
|
+
|
|
87
|
+
获取节点属性,或获取节点上某个组件的属性。
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
cocos2d-cli get <文件> <节点路径> [属性名 | 组件类型]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
说明:
|
|
94
|
+
|
|
95
|
+
- 不传第三个参数:返回该节点全部基础属性
|
|
96
|
+
- 第三个参数是组件类型:返回该组件全部属性
|
|
97
|
+
- 第三个参数是属性名:返回该节点的单个属性
|
|
98
|
+
|
|
99
|
+
示例:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
cocos2d-cli get assets/main.fire Canvas
|
|
103
|
+
cocos2d-cli get assets/main.fire Canvas/MidNode x
|
|
104
|
+
cocos2d-cli get assets/main.fire Canvas/Label Label
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### set
|
|
108
|
+
|
|
109
|
+
修改节点属性。
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
cocos2d-cli set <文件> <节点路径> <属性名> <值>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
当前支持的常用节点属性:
|
|
116
|
+
|
|
117
|
+
- `name`
|
|
118
|
+
- `active`
|
|
119
|
+
- `x`
|
|
120
|
+
- `y`
|
|
121
|
+
- `width`
|
|
122
|
+
- `height`
|
|
123
|
+
- `anchorX`
|
|
124
|
+
- `anchorY`
|
|
125
|
+
- `opacity`
|
|
126
|
+
- `scaleX`
|
|
127
|
+
- `scaleY`
|
|
128
|
+
- `rotation`
|
|
129
|
+
|
|
130
|
+
示例:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
cocos2d-cli set assets/main.fire Canvas/MidNode x 100
|
|
134
|
+
cocos2d-cli set assets/main.fire Canvas/MidNode width 200
|
|
135
|
+
cocos2d-cli set assets/main.fire Canvas/MidNode active false
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### set-component
|
|
139
|
+
|
|
140
|
+
修改节点上某个组件的属性。
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
cocos2d-cli set-component <文件> <节点路径> <组件类型> <属性名> <值>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
示例:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
cocos2d-cli set-component assets/main.fire Canvas/Label Label string "Hello"
|
|
150
|
+
cocos2d-cli set-component assets/main.fire Canvas/Label Label fontSize 32
|
|
151
|
+
cocos2d-cli set-component assets/main.fire Canvas/Sprite Sprite sizeMode 0
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
> 不同组件是否支持修改,取决于该组件类是否实现了 `setProp`。
|
|
155
|
+
|
|
156
|
+
### add
|
|
157
|
+
|
|
158
|
+
在指定父节点下添加新节点。
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
cocos2d-cli add <文件> <父节点路径> <节点名称> [选项]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
支持选项:
|
|
165
|
+
|
|
166
|
+
- `--x=<数值>`
|
|
167
|
+
- `--y=<数值>`
|
|
168
|
+
- `--width=<数值>`
|
|
169
|
+
- `--height=<数值>`
|
|
170
|
+
- `--scaleX=<数值>`
|
|
171
|
+
- `--scaleY=<数值>`
|
|
172
|
+
- `--rotation=<角度>`
|
|
173
|
+
- `--active=true|false`
|
|
174
|
+
- `--type=sprite|label|button`
|
|
175
|
+
- `--string=<文本>`(配合 `--type=label`)
|
|
176
|
+
- `--fontSize=<数值>`(配合 `--type=label`)
|
|
177
|
+
|
|
178
|
+
示例:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
cocos2d-cli add assets/main.fire Canvas NewSprite --type=sprite --x=100 --y=50 --width=120 --height=120
|
|
182
|
+
cocos2d-cli add assets/main.fire Canvas Title --type=label --string=Hello --fontSize=32
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### remove
|
|
186
|
+
|
|
187
|
+
删除指定节点。
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
cocos2d-cli remove <文件> <节点路径>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
示例:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
cocos2d-cli remove assets/main.fire Canvas/MidNode
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
> 不能删除根节点。
|
|
200
|
+
|
|
201
|
+
### add-component
|
|
202
|
+
|
|
203
|
+
给节点添加组件。
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
cocos2d-cli add-component <文件> <节点路径> <组件类型>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
当前命令中支持的组件类型:
|
|
210
|
+
|
|
211
|
+
- `canvas`
|
|
212
|
+
- `widget`
|
|
213
|
+
- `sprite`
|
|
214
|
+
- `label`
|
|
215
|
+
- `button`
|
|
216
|
+
- `camera`
|
|
217
|
+
|
|
218
|
+
示例:
|
|
20
219
|
|
|
21
220
|
```bash
|
|
22
|
-
cocos2d-cli
|
|
23
|
-
cocos2d-cli
|
|
221
|
+
cocos2d-cli add-component assets/main.fire Canvas/NewNode sprite
|
|
222
|
+
cocos2d-cli add-component assets/main.fire Canvas/Main Camera camera
|
|
24
223
|
```
|
|
25
224
|
|
|
26
|
-
###
|
|
225
|
+
### remove-component
|
|
226
|
+
|
|
227
|
+
删除节点上的组件。
|
|
27
228
|
|
|
28
|
-
|
|
229
|
+
```bash
|
|
230
|
+
cocos2d-cli remove-component <文件> <节点路径> <组件类型>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
示例:
|
|
29
234
|
|
|
30
235
|
```bash
|
|
31
|
-
|
|
32
|
-
|
|
236
|
+
cocos2d-cli remove-component assets/main.fire Canvas/NewNode sprite
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### create-prefab
|
|
33
240
|
|
|
34
|
-
|
|
35
|
-
|
|
241
|
+
创建预制体。
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
cocos2d-cli create-prefab [JSON文件路径] <输出路径.prefab>
|
|
36
245
|
```
|
|
37
246
|
|
|
38
|
-
|
|
247
|
+
行为说明:
|
|
248
|
+
|
|
249
|
+
- 只传输出路径:创建默认预制体
|
|
250
|
+
- 传入 JSON 文件:根据 JSON 创建预制体
|
|
251
|
+
- 会自动生成对应 `.meta` 文件
|
|
252
|
+
|
|
253
|
+
示例:
|
|
39
254
|
|
|
40
255
|
```bash
|
|
41
|
-
|
|
42
|
-
cocos2d-cli create-
|
|
256
|
+
cocos2d-cli create-prefab assets/NewNode.prefab
|
|
257
|
+
cocos2d-cli create-prefab panel.json assets/Panel.prefab
|
|
258
|
+
```
|
|
43
259
|
|
|
44
|
-
|
|
45
|
-
|
|
260
|
+
### create-scene
|
|
261
|
+
|
|
262
|
+
创建场景。
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
cocos2d-cli create-scene [JSON文件路径] <输出路径.fire>
|
|
46
266
|
```
|
|
47
267
|
|
|
48
|
-
|
|
268
|
+
行为说明:
|
|
269
|
+
|
|
270
|
+
- 只传输出路径:创建默认场景
|
|
271
|
+
- 默认场景包含 `Canvas` 和 `Main Camera`
|
|
272
|
+
- 若能在上级目录找到 `settings/project.json`,会读取设计分辨率
|
|
273
|
+
- 会自动生成对应 `.meta` 文件
|
|
274
|
+
|
|
275
|
+
示例:
|
|
49
276
|
|
|
50
277
|
```bash
|
|
51
|
-
|
|
52
|
-
cocos2d-cli
|
|
278
|
+
cocos2d-cli create-scene assets/Main.fire
|
|
279
|
+
cocos2d-cli create-scene scene.json assets/Scene.fire
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### build
|
|
53
283
|
|
|
54
|
-
|
|
55
|
-
cocos2d-cli remove ./MyPrefab.prefab Root/NewNode
|
|
284
|
+
扫描 Cocos Creator 项目的 `library/imports`,构建脚本哈希到类名的映射文件,用于更好地识别自定义脚本组件。
|
|
56
285
|
|
|
57
|
-
|
|
58
|
-
cocos2d-cli
|
|
286
|
+
```bash
|
|
287
|
+
cocos2d-cli build <项目目录>
|
|
59
288
|
```
|
|
60
289
|
|
|
61
|
-
|
|
290
|
+
示例:
|
|
62
291
|
|
|
63
292
|
```bash
|
|
64
|
-
|
|
65
|
-
|
|
293
|
+
cocos2d-cli build D:/my-cocos-project
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
输出文件默认写到:
|
|
66
297
|
|
|
67
|
-
|
|
68
|
-
cocos2d-cli
|
|
298
|
+
```text
|
|
299
|
+
cocos2d-cli/data/script_map.json
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### screenshot
|
|
303
|
+
|
|
304
|
+
将 JSON 渲染为页面并截图。
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
cocos2d-cli screenshot <json文件> [选项]
|
|
69
308
|
```
|
|
70
309
|
|
|
71
|
-
|
|
310
|
+
支持选项:
|
|
311
|
+
|
|
312
|
+
- `-o, --output <目录>` 输出目录,默认当前目录
|
|
313
|
+
- `--width <数值>` 视口宽度,默认 `750`
|
|
314
|
+
- `--height <数值>` 视口高度,默认 `1334`
|
|
315
|
+
- `--full-page` 全页截图(默认)
|
|
316
|
+
- `--no-full-page` 仅视口截图
|
|
317
|
+
- `--debug-bounds` 叠加节点边界和名称
|
|
318
|
+
- `--timeout <毫秒>` 默认 `30000`
|
|
319
|
+
- `--wait <毫秒>` 默认 `1000`
|
|
320
|
+
|
|
321
|
+
示例:
|
|
72
322
|
|
|
73
323
|
```bash
|
|
74
|
-
|
|
75
|
-
cocos2d-cli
|
|
76
|
-
cocos2d-cli
|
|
324
|
+
cocos2d-cli screenshot data.json
|
|
325
|
+
cocos2d-cli screenshot data.json -o ./screenshots
|
|
326
|
+
cocos2d-cli screenshot data.json --width 1080 --height 1920
|
|
327
|
+
cocos2d-cli screenshot data.json --debug-bounds
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
> `-o` / `--output` 是输出目录,不是输出图片文件名。
|
|
331
|
+
|
|
332
|
+
## 节点路径格式
|
|
77
333
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
334
|
+
节点路径使用 `/` 分隔:
|
|
335
|
+
|
|
336
|
+
```text
|
|
337
|
+
Canvas
|
|
338
|
+
Canvas/MidNode
|
|
339
|
+
Canvas/GameScene/NodeA
|
|
81
340
|
```
|
|
82
341
|
|
|
83
|
-
|
|
342
|
+
如果路径首段和根节点同名,工具会自动兼容。
|
|
343
|
+
|
|
344
|
+
## JSON 格式
|
|
84
345
|
|
|
85
|
-
|
|
346
|
+
### 基本示例
|
|
86
347
|
|
|
87
348
|
```json
|
|
88
349
|
{
|
|
89
|
-
"name": "
|
|
90
|
-
"width":
|
|
91
|
-
"height":
|
|
92
|
-
"
|
|
350
|
+
"name": "Panel",
|
|
351
|
+
"width": 400,
|
|
352
|
+
"height": 300,
|
|
353
|
+
"x": 0,
|
|
354
|
+
"y": 0,
|
|
355
|
+
"anchorX": 0.5,
|
|
356
|
+
"anchorY": 0.5,
|
|
357
|
+
"color": "#336699",
|
|
358
|
+
"opacity": 255,
|
|
359
|
+
"components": [
|
|
360
|
+
"sprite",
|
|
361
|
+
{ "type": "label", "string": "Hello", "fontSize": 28, "horizontalAlign": "center" }
|
|
362
|
+
],
|
|
93
363
|
"children": [
|
|
94
364
|
{
|
|
95
|
-
"name": "
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"string": "Click Me",
|
|
100
|
-
"fontSize": 24
|
|
101
|
-
}
|
|
102
|
-
]
|
|
365
|
+
"name": "Btn",
|
|
366
|
+
"width": 200,
|
|
367
|
+
"height": 60,
|
|
368
|
+
"components": ["sprite", "button"]
|
|
103
369
|
}
|
|
104
370
|
]
|
|
105
371
|
}
|
|
106
372
|
```
|
|
107
373
|
|
|
108
|
-
###
|
|
374
|
+
### 常用节点字段
|
|
375
|
+
|
|
376
|
+
- `name`
|
|
377
|
+
- `x`, `y`
|
|
378
|
+
- `width`, `height`
|
|
379
|
+
- `anchorX`, `anchorY`
|
|
380
|
+
- `scaleX`, `scaleY`
|
|
381
|
+
- `rotation`
|
|
382
|
+
- `opacity`
|
|
383
|
+
- `active`
|
|
384
|
+
- `color`
|
|
385
|
+
- `components`
|
|
386
|
+
- `children`
|
|
387
|
+
|
|
388
|
+
### JSON 中支持的组件类型
|
|
389
|
+
|
|
390
|
+
- `canvas`
|
|
391
|
+
- `widget`
|
|
392
|
+
- `sprite`
|
|
393
|
+
- `label`
|
|
394
|
+
- `button`
|
|
395
|
+
- `camera`
|
|
396
|
+
- `richText`
|
|
397
|
+
|
|
398
|
+
### 组件示例
|
|
399
|
+
|
|
400
|
+
`label`:
|
|
401
|
+
|
|
402
|
+
```json
|
|
403
|
+
{
|
|
404
|
+
"type": "label",
|
|
405
|
+
"string": "文本内容",
|
|
406
|
+
"fontSize": 28,
|
|
407
|
+
"lineHeight": 40,
|
|
408
|
+
"horizontalAlign": "left",
|
|
409
|
+
"verticalAlign": "center",
|
|
410
|
+
"color": "#ffffff"
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
`richText`:
|
|
415
|
+
|
|
416
|
+
```json
|
|
417
|
+
{
|
|
418
|
+
"type": "richText",
|
|
419
|
+
"string": "普通<color=#ff0000>红色</color>文字<br/>第二行",
|
|
420
|
+
"fontSize": 28,
|
|
421
|
+
"lineHeight": 40,
|
|
422
|
+
"maxWidth": 600,
|
|
423
|
+
"horizontalAlign": "left"
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
`widget`:
|
|
428
|
+
|
|
429
|
+
```json
|
|
430
|
+
{
|
|
431
|
+
"type": "widget",
|
|
432
|
+
"isAlignLeft": true,
|
|
433
|
+
"isAlignRight": true,
|
|
434
|
+
"isAlignTop": true,
|
|
435
|
+
"isAlignBottom": true,
|
|
436
|
+
"left": 0,
|
|
437
|
+
"right": 0,
|
|
438
|
+
"top": 0,
|
|
439
|
+
"bottom": 0
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## 坐标与布局说明
|
|
109
444
|
|
|
110
|
-
|
|
111
|
-
- `widget` - Widget
|
|
112
|
-
- `sprite` - Sprite
|
|
113
|
-
- `label` - Label
|
|
114
|
-
- `button` - Button
|
|
115
|
-
- `camera` - Camera
|
|
445
|
+
Cocos 默认锚点在节点中心:
|
|
116
446
|
|
|
117
|
-
|
|
447
|
+
- `anchorX = 0.5`
|
|
448
|
+
- `anchorY = 0.5`
|
|
118
449
|
|
|
119
|
-
|
|
120
|
-
|------|------|
|
|
121
|
-
| `name` | 节点名称 |
|
|
122
|
-
| `active` | 是否激活 |
|
|
123
|
-
| `x`, `y` | 位置 |
|
|
124
|
-
| `width`, `height` | 尺寸 |
|
|
125
|
-
| `scaleX`, `scaleY` | 缩放 |
|
|
126
|
-
| `rotation` | 旋转角度 |
|
|
127
|
-
| `anchorX`, `anchorY` | 锚点 |
|
|
128
|
-
| `opacity` | 透明度 |
|
|
129
|
-
| `color` | 颜色 (十六进制如 "#ff0000") |
|
|
450
|
+
因此 `x / y` 通常是相对父节点中心点的偏移。
|
|
130
451
|
|
|
131
|
-
|
|
452
|
+
对于左对齐 / 右对齐文本,更推荐:
|
|
132
453
|
|
|
454
|
+
- `anchorX: 0` + `horizontalAlign: "left"`
|
|
455
|
+
- `anchorX: 1` + `horizontalAlign: "right"`
|
|
456
|
+
|
|
457
|
+
示例:
|
|
458
|
+
|
|
459
|
+
```json
|
|
460
|
+
{
|
|
461
|
+
"name": "Amount",
|
|
462
|
+
"anchorX": 1,
|
|
463
|
+
"x": 330,
|
|
464
|
+
"width": 300,
|
|
465
|
+
"components": [
|
|
466
|
+
{ "type": "label", "string": "¥40.00", "horizontalAlign": "right" }
|
|
467
|
+
]
|
|
468
|
+
}
|
|
133
469
|
```
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
470
|
+
|
|
471
|
+
## 注意事项
|
|
472
|
+
|
|
473
|
+
1. JSON 输入必须是文件路径,不支持直接传 JSON 字符串
|
|
474
|
+
2. `screenshot` 的输出参数是目录,不是图片路径
|
|
475
|
+
3. `set-component` 是否可修改,取决于组件类是否实现 `setProp`
|
|
476
|
+
4. `create-scene` 不传 JSON 时,会创建默认场景(含 `Canvas` 和 `Main Camera`)
|
|
477
|
+
5. `create-prefab` / `create-scene` 会自动写入 `.meta` 文件
|
|
478
|
+
6. `build` 生成的脚本映射会写入项目内的 `data/script_map.json`
|
|
479
|
+
|
|
480
|
+
## 项目结构
|
|
481
|
+
|
|
482
|
+
```text
|
|
483
|
+
bin/cocos2d-cli.js # CLI 入口
|
|
484
|
+
src/commands/ # 各命令实现
|
|
485
|
+
src/lib/ # 场景/节点/组件处理逻辑
|
|
486
|
+
data/script_map.json # 脚本组件映射
|
|
138
487
|
```
|
|
139
488
|
|
|
140
489
|
## License
|
|
141
490
|
|
|
142
|
-
MIT
|
|
491
|
+
MIT
|
package/package.json
CHANGED
|
@@ -89,10 +89,13 @@ async function run(args) {
|
|
|
89
89
|
options.outputDir = path.resolve(options.outputDir);
|
|
90
90
|
|
|
91
91
|
try {
|
|
92
|
-
|
|
93
|
-
console.log(
|
|
92
|
+
await takeScreenshot(options);
|
|
93
|
+
console.log('成功');
|
|
94
94
|
} catch (error) {
|
|
95
|
-
console.error(
|
|
95
|
+
console.error('失败');
|
|
96
|
+
if (error.logDir) {
|
|
97
|
+
console.error(`日志目录: ${error.logDir}`);
|
|
98
|
+
}
|
|
96
99
|
process.exit(1);
|
|
97
100
|
}
|
|
98
101
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Screenshot Core Module
|
|
2
|
+
* Screenshot Core Module
|
|
3
3
|
* 渲染 JSON 数据并使用 Playwright 截图
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -36,7 +36,7 @@ async function createTempWorkDir() {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// 复制内置资源到临时目录
|
|
39
|
-
async function copyBuiltInAssets(tempDir) {
|
|
39
|
+
async function copyBuiltInAssets(tempDir, logs) {
|
|
40
40
|
const assetsDir = getAssetsDir();
|
|
41
41
|
const assets = ['index.html', 'favicon.ico'];
|
|
42
42
|
|
|
@@ -46,13 +46,17 @@ async function copyBuiltInAssets(tempDir) {
|
|
|
46
46
|
try {
|
|
47
47
|
await fs.copyFile(src, dest);
|
|
48
48
|
} catch (err) {
|
|
49
|
-
|
|
49
|
+
logs.push({
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
type: 'warning',
|
|
52
|
+
text: `Could not copy ${asset}: ${err.message}`
|
|
53
|
+
});
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
// 静态文件服务器
|
|
55
|
-
async function startServer(staticDir) {
|
|
59
|
+
async function startServer(staticDir, logs) {
|
|
56
60
|
return new Promise((resolve, reject) => {
|
|
57
61
|
const server = http.createServer(async (req, res) => {
|
|
58
62
|
try {
|
|
@@ -94,7 +98,11 @@ async function startServer(staticDir) {
|
|
|
94
98
|
res.writeHead(404);
|
|
95
99
|
res.end();
|
|
96
100
|
} else {
|
|
97
|
-
|
|
101
|
+
logs.push({
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
type: 'server-error',
|
|
104
|
+
text: `${req.url}: ${err.message}`
|
|
105
|
+
});
|
|
98
106
|
res.writeHead(500);
|
|
99
107
|
res.end();
|
|
100
108
|
}
|
|
@@ -106,11 +114,17 @@ async function startServer(staticDir) {
|
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
// 递归删除目录
|
|
109
|
-
async function removeDir(dirPath) {
|
|
117
|
+
async function removeDir(dirPath, logs) {
|
|
110
118
|
try {
|
|
111
119
|
await fs.rm(dirPath, { recursive: true, force: true });
|
|
112
120
|
} catch (err) {
|
|
113
|
-
|
|
121
|
+
if (logs) {
|
|
122
|
+
logs.push({
|
|
123
|
+
timestamp: new Date().toISOString(),
|
|
124
|
+
type: 'warning',
|
|
125
|
+
text: `Could not remove temp dir: ${err.message}`
|
|
126
|
+
});
|
|
127
|
+
}
|
|
114
128
|
}
|
|
115
129
|
}
|
|
116
130
|
|
|
@@ -137,6 +151,16 @@ async function takeScreenshot(userConfig = {}) {
|
|
|
137
151
|
let tempDir = null;
|
|
138
152
|
const logs = [];
|
|
139
153
|
let screenshotPath = null;
|
|
154
|
+
let logDir = null;
|
|
155
|
+
|
|
156
|
+
const addLog = (type, text, extra = {}) => {
|
|
157
|
+
logs.push({
|
|
158
|
+
timestamp: new Date().toISOString(),
|
|
159
|
+
type,
|
|
160
|
+
text,
|
|
161
|
+
...extra
|
|
162
|
+
});
|
|
163
|
+
};
|
|
140
164
|
|
|
141
165
|
try {
|
|
142
166
|
const timestamp = Date.now();
|
|
@@ -153,32 +177,32 @@ async function takeScreenshot(userConfig = {}) {
|
|
|
153
177
|
|
|
154
178
|
// 创建临时工作目录
|
|
155
179
|
tempDir = await createTempWorkDir();
|
|
156
|
-
|
|
180
|
+
addLog('info', `Temp dir: ${tempDir}`);
|
|
157
181
|
|
|
158
182
|
// 复制内置资源到临时目录
|
|
159
|
-
await copyBuiltInAssets(tempDir);
|
|
160
|
-
|
|
183
|
+
await copyBuiltInAssets(tempDir, logs);
|
|
184
|
+
|
|
161
185
|
// 复制用户的 JSON 文件到临时目录
|
|
162
186
|
const destJsonPath = path.join(tempDir, 'data.json');
|
|
163
187
|
await fs.copyFile(config.jsonPath, destJsonPath);
|
|
164
|
-
|
|
188
|
+
addLog('info', `JSON: ${config.jsonPath}`);
|
|
165
189
|
|
|
166
190
|
// 截图输出路径
|
|
167
191
|
screenshotPath = path.join(config.outputDir, `screenshot-${timestamp}.png`);
|
|
168
192
|
|
|
169
193
|
// 启动HTTP服务器
|
|
170
|
-
|
|
171
|
-
server = await startServer(tempDir);
|
|
194
|
+
addLog('info', 'Starting Server');
|
|
195
|
+
server = await startServer(tempDir, logs);
|
|
172
196
|
const serverUrl = `http://127.0.0.1:${server.address().port}`;
|
|
173
|
-
|
|
197
|
+
addLog('info', `Server: ${serverUrl}`);
|
|
174
198
|
|
|
175
199
|
// 启动浏览器
|
|
176
|
-
|
|
200
|
+
addLog('info', 'Launching Browser');
|
|
177
201
|
browser = await chromium.launch({
|
|
178
202
|
headless: true,
|
|
179
203
|
channel: 'chrome'
|
|
180
204
|
});
|
|
181
|
-
|
|
205
|
+
addLog('info', 'Browser launched');
|
|
182
206
|
|
|
183
207
|
const page = await browser.newPage({
|
|
184
208
|
viewport: config.viewport
|
|
@@ -187,44 +211,21 @@ async function takeScreenshot(userConfig = {}) {
|
|
|
187
211
|
// 监听浏览器控制台日志
|
|
188
212
|
page.on('console', msg => {
|
|
189
213
|
const text = msg.text();
|
|
190
|
-
|
|
191
|
-
timestamp: new Date().toISOString(),
|
|
192
|
-
type: msg.type(),
|
|
193
|
-
text
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
if (msg.type() === 'error') {
|
|
197
|
-
console.error(`[Browser Error] ${text}`);
|
|
198
|
-
} else if (msg.type() === 'warning') {
|
|
199
|
-
console.warn(`[Browser Warning] ${text}`);
|
|
200
|
-
} else {
|
|
201
|
-
console.log(`[Browser] ${text}`);
|
|
202
|
-
}
|
|
214
|
+
addLog(msg.type(), text);
|
|
203
215
|
});
|
|
204
216
|
|
|
205
217
|
page.on('pageerror', error => {
|
|
206
|
-
|
|
207
|
-
timestamp: new Date().toISOString(),
|
|
208
|
-
type: 'pageerror',
|
|
209
|
-
text: error.message
|
|
210
|
-
});
|
|
211
|
-
console.error('[Page Error]', error.message);
|
|
218
|
+
addLog('pageerror', error.message);
|
|
212
219
|
});
|
|
213
220
|
|
|
214
221
|
page.on('requestfailed', request => {
|
|
215
222
|
const failure = request.failure();
|
|
216
223
|
const errorText = failure ? failure.errorText : 'Unknown error';
|
|
217
|
-
|
|
218
|
-
timestamp: new Date().toISOString(),
|
|
219
|
-
type: 'requestfailed',
|
|
220
|
-
url: request.url(),
|
|
221
|
-
error: errorText
|
|
222
|
-
});
|
|
223
|
-
console.error(`[Request Failed] ${request.url()}`);
|
|
224
|
+
addLog('requestfailed', request.url(), { error: errorText });
|
|
224
225
|
});
|
|
225
226
|
|
|
226
227
|
// 加载页面
|
|
227
|
-
|
|
228
|
+
addLog('info', 'Loading Page');
|
|
228
229
|
const pageUrl = config.debugBounds
|
|
229
230
|
? `${serverUrl}/index.html?debugBounds=true`
|
|
230
231
|
: `${serverUrl}/index.html`;
|
|
@@ -232,33 +233,53 @@ async function takeScreenshot(userConfig = {}) {
|
|
|
232
233
|
waitUntil: 'networkidle',
|
|
233
234
|
timeout: config.timeout
|
|
234
235
|
});
|
|
235
|
-
|
|
236
|
+
addLog('info', 'Page loaded');
|
|
236
237
|
|
|
237
238
|
// 等待渲染
|
|
238
|
-
|
|
239
|
+
addLog('info', 'Waiting for Render');
|
|
239
240
|
await page.waitForTimeout(config.waitTime);
|
|
240
|
-
|
|
241
|
+
addLog('info', 'Wait complete');
|
|
241
242
|
|
|
242
243
|
// 截图
|
|
243
|
-
|
|
244
|
+
addLog('info', 'Taking Screenshot');
|
|
244
245
|
await page.screenshot({
|
|
245
246
|
path: screenshotPath,
|
|
246
247
|
fullPage: config.fullPage
|
|
247
248
|
});
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
console.log('\n=== Done ===');
|
|
249
|
+
addLog('info', `Screenshot saved: ${screenshotPath}`);
|
|
250
|
+
addLog('info', 'Done');
|
|
251
251
|
|
|
252
252
|
return { screenshotPath, logs };
|
|
253
253
|
|
|
254
254
|
} catch (error) {
|
|
255
|
-
|
|
255
|
+
addLog('error', error.message);
|
|
256
|
+
try {
|
|
257
|
+
logDir = path.join(config.outputDir, `screenshot-logs-${Date.now()}`);
|
|
258
|
+
await fs.mkdir(logDir, { recursive: true });
|
|
259
|
+
await fs.writeFile(
|
|
260
|
+
path.join(logDir, 'logs.json'),
|
|
261
|
+
JSON.stringify({
|
|
262
|
+
error: error.message,
|
|
263
|
+
jsonPath: config.jsonPath,
|
|
264
|
+
outputDir: config.outputDir,
|
|
265
|
+
viewport: config.viewport,
|
|
266
|
+
fullPage: config.fullPage,
|
|
267
|
+
debugBounds: config.debugBounds,
|
|
268
|
+
timeout: config.timeout,
|
|
269
|
+
waitTime: config.waitTime,
|
|
270
|
+
screenshotPath,
|
|
271
|
+
logs
|
|
272
|
+
}, null, 2),
|
|
273
|
+
'utf8'
|
|
274
|
+
);
|
|
275
|
+
} catch (_) {}
|
|
276
|
+
error.logDir = logDir;
|
|
256
277
|
throw error;
|
|
257
278
|
} finally {
|
|
258
279
|
// 清理资源
|
|
259
280
|
if (browser) await browser.close().catch(() => {});
|
|
260
281
|
if (server) server.close();
|
|
261
|
-
if (tempDir) await removeDir(tempDir);
|
|
282
|
+
if (tempDir) await removeDir(tempDir, logs);
|
|
262
283
|
}
|
|
263
284
|
}
|
|
264
285
|
|