@pixui-dev/pxw 0.1.21 → 0.1.23

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 (95) hide show
  1. package/bin/pxw.js +192 -199
  2. package/bin/wpbuild.js +10 -10
  3. package/config/default.conf +20 -20
  4. package/config/devops.js +360 -372
  5. package/config/h5es.js +10 -10
  6. package/config/index.html +183 -183
  7. package/config/pfbs.js +251 -245
  8. package/config/util.js +136 -140
  9. package/config/webpack.js +324 -325
  10. package/lib/assets/check.html +62 -62
  11. package/lib/assets/preact.js +4354 -4354
  12. package/lib/check/main.less +62 -62
  13. package/lib/check/main.tsx +41 -41
  14. package/lib/check/tool.js +3 -3
  15. package/lib/check/util.tsx +110 -110
  16. package/lib/grpc-web/dist/ChunkParser.js +117 -117
  17. package/lib/grpc-web/dist/Code.js +58 -58
  18. package/lib/grpc-web/dist/client.js +299 -299
  19. package/lib/grpc-web/dist/debug.js +16 -16
  20. package/lib/grpc-web/dist/detach.js +7 -7
  21. package/lib/grpc-web/dist/index.js +29 -29
  22. package/lib/grpc-web/dist/invoke.js +32 -32
  23. package/lib/grpc-web/dist/message.js +3 -3
  24. package/lib/grpc-web/dist/metadata.js +5 -5
  25. package/lib/grpc-web/dist/service.js +3 -3
  26. package/lib/grpc-web/dist/transports/Transport.js +15 -15
  27. package/lib/grpc-web/dist/transports/http/fetch.js +117 -117
  28. package/lib/grpc-web/dist/transports/http/http.js +15 -15
  29. package/lib/grpc-web/dist/transports/http/xhr.js +136 -136
  30. package/lib/grpc-web/dist/transports/http/xhrUtil.js +36 -36
  31. package/lib/grpc-web/dist/transports/websocket/websocket.js +95 -95
  32. package/lib/grpc-web/dist/typings/ChunkParser.d.ts +17 -17
  33. package/lib/grpc-web/dist/typings/Code.d.ts +20 -20
  34. package/lib/grpc-web/dist/typings/client.d.ts +25 -25
  35. package/lib/grpc-web/dist/typings/debug.d.ts +1 -1
  36. package/lib/grpc-web/dist/typings/detach.d.ts +1 -1
  37. package/lib/grpc-web/dist/typings/index.d.ts +45 -45
  38. package/lib/grpc-web/dist/typings/invoke.d.ts +20 -20
  39. package/lib/grpc-web/dist/typings/message.d.ts +8 -8
  40. package/lib/grpc-web/dist/typings/metadata.d.ts +2 -2
  41. package/lib/grpc-web/dist/typings/service.d.ts +16 -16
  42. package/lib/grpc-web/dist/typings/transports/Transport.d.ts +22 -22
  43. package/lib/grpc-web/dist/typings/transports/http/fetch.d.ts +6 -6
  44. package/lib/grpc-web/dist/typings/transports/http/http.d.ts +5 -5
  45. package/lib/grpc-web/dist/typings/transports/http/xhr.d.ts +27 -27
  46. package/lib/grpc-web/dist/typings/transports/http/xhrUtil.d.ts +3 -3
  47. package/lib/grpc-web/dist/typings/transports/websocket/websocket.d.ts +2 -2
  48. package/lib/grpc-web/dist/typings/unary.d.ts +23 -23
  49. package/lib/grpc-web/dist/typings/util.d.ts +2 -2
  50. package/lib/grpc-web/dist/unary.js +44 -44
  51. package/lib/grpc-web/dist/util.js +11 -11
  52. package/lib/grpcTransport/PixHttp2Transport.ts +107 -107
  53. package/lib/grpcTransport/PixLuaTransport.ts +82 -82
  54. package/lib/h5es-types/v1.9.2/h5es.d.ts +1698 -1698
  55. package/lib/h5es-types/v3.5.0/h5es.d.ts +1788 -1788
  56. package/lib/pi_component/tinyList/tinyList.js +483 -483
  57. package/lib/pi_component/tinyList/tinyList.tsx +517 -517
  58. package/lib/preact-router.js +395 -395
  59. package/lib/preact.js +4355 -4355
  60. package/lib/preact.tq.js +4385 -4385
  61. package/lib/react-window/src/FixedSizeGrid.js +172 -172
  62. package/lib/react-window/src/FixedSizeList.js +91 -91
  63. package/lib/react-window/src/VariableSizeGrid.js +329 -329
  64. package/lib/react-window/src/VariableSizeList.js +231 -231
  65. package/lib/react-window/src/__tests__/FixedSizeGrid.js +942 -942
  66. package/lib/react-window/src/__tests__/FixedSizeList.js +749 -749
  67. package/lib/react-window/src/__tests__/VariableSizeGrid.js +598 -598
  68. package/lib/react-window/src/__tests__/VariableSizeList.js +345 -345
  69. package/lib/react-window/src/__tests__/__snapshots__/FixedSizeGrid.js.snap +912 -912
  70. package/lib/react-window/src/__tests__/__snapshots__/FixedSizeList.js.snap +568 -568
  71. package/lib/react-window/src/__tests__/__snapshots__/VariableSizeGrid.js.snap +542 -542
  72. package/lib/react-window/src/__tests__/__snapshots__/VariableSizeList.js.snap +331 -331
  73. package/lib/react-window/src/__tests__/areEqual.js +28 -28
  74. package/lib/react-window/src/__tests__/shouldComponentUpdate.js +32 -32
  75. package/lib/react-window/src/areEqual.js +13 -13
  76. package/lib/react-window/src/createGridComponent.js +657 -657
  77. package/lib/react-window/src/createListComponent.js +574 -574
  78. package/lib/react-window/src/domHelpers.js +69 -69
  79. package/lib/react-window/src/index.js +9 -9
  80. package/lib/react-window/src/shallowDiffers.js +17 -17
  81. package/lib/react-window/src/shouldComponentUpdate.js +11 -11
  82. package/lib/react-window/src/test.js.flow +382 -382
  83. package/lib/react-window/src/timer.js +36 -36
  84. package/lib/types/dom.ts +17 -17
  85. package/lib/types/ext.d.ts +81 -81
  86. package/lib/types/preact/css.d.ts +7476 -7476
  87. package/lib/types/preact/index.d.ts +340 -340
  88. package/lib/types/preact/internal.d.ts +94 -94
  89. package/lib/types/preact/jsx.d.ts +320 -309
  90. package/lib/types/preact-router/index.d.ts +84 -84
  91. package/package.json +114 -113
  92. package/scripts/pack.js +40 -40
  93. package/scripts/postinstall.js +12 -12
  94. package/scripts/run-pxw.js +13 -13
  95. package/tsconfig.json +30 -30
package/config/devops.js CHANGED
@@ -1,372 +1,360 @@
1
- let ws = require('ws');
2
- let fs = require('fs');
3
- let cp = require('child_process');
4
- let http = require('http');
5
- let path = require('path');
6
- let pr = path.resolve;
7
-
8
- let multer = require('multer');
9
- let express = require('express');
10
-
11
- let rootDir = pr(__dirname, '../');
12
-
13
- let dirConfig = {
14
- pxkit: '',
15
- pxembed: '',
16
- pxapp: '',
17
- };
18
-
19
- function updateFile(file, targetLine) {
20
- targetLine = '\n' + targetLine;
21
- fs.existsSync(file) ? fs.readFileSync(file, 'utf8').includes(targetLine) || fs.appendFileSync(file, targetLine) : fs.writeFileSync(file, targetLine);
22
- }
23
-
24
- module.exports.setupDevops = async function (server, app) {
25
- let si = require('systeminformation');
26
- let sys = await si.system();
27
- let runInVM = /Parallels/.test(sys.manufacturer);
28
- console.log(`system manufacturer:${sys.manufacturer}, runInVM:${runInVM}`);
29
-
30
- let watcher;
31
-
32
- if (!runInVM) {
33
- let chokidar = require('chokidar');
34
- watcher = chokidar.watch(
35
- ['/**/*.html', '/**/*.js'].map((v) => pr('.') + v),
36
- { ignored: [/\.build\//, /node_modules/], persistent: true },
37
- );
38
-
39
- watcher
40
- .on('change', function (path) {
41
- console.log('changetest', path);
42
- })
43
- .on('error', function (error) {
44
- console.error('Error happened', error);
45
- });
46
- }
47
-
48
- {
49
- // let pp = cp.execSync('yarn', { cwd: rootDir + '/pxtest/server/', stdio: 'pipe' });
50
- // console.log('yarn for pxtest server:\n', pp.toString());
51
- }
52
-
53
- function notifyAppReload(htmlPath) {
54
- if (!watcher) return;
55
- watcher.emit('change', htmlPath);
56
- }
57
-
58
- let subProcesses = {};
59
- let devopsConn;
60
- let websocketServer = new ws.Server({ server });
61
- websocketServer.on('connection', (conn, req) => {
62
- let url = new URL(req.url, 'http://localhost');
63
- //去掉第1个字符/,否则在windows上与path比较时会因为\而不一样
64
- let referrer = url.searchParams.get('referrer')?.substring(1);
65
- console.log('ws connection', url.pathname, referrer);
66
- // console.log(url.pathname, url.searchParams);
67
- if (url.pathname == '/notify') {
68
- conn.on('message', (message) => {
69
- // client.send(`{ "message" : ${message} }`);
70
- });
71
- let changeHandler;
72
- watcher?.on(
73
- 'change',
74
- (changeHandler = (path) => {
75
- //只响应该页面对应的文件变化
76
- console.log('filechange check', path, referrer);
77
- if (
78
- path.endsWith(referrer) || //这种情况是静态html文件,只比较尾部相等
79
- referrer.startsWith(path) //这种情况是webpack里的app,排除用来作route的后段,只比较首段相等
80
- ) {
81
- console.log('reload', path);
82
- conn.send('reload');
83
- }
84
- }),
85
- );
86
- conn.on('close', () => {
87
- watcher?.removeListener('change', changeHandler);
88
- console.log('ws connection closed', url.pathname, url.searchParams);
89
- });
90
- }
91
- else if (url.pathname == '/devops') {
92
- devopsConn = conn;
93
- let notifySubProcesses = () => {
94
- conn.send(
95
- JSON.stringify({
96
- type: 'subProcesses',
97
- data: Object.entries(subProcesses).map(([k, v]) => {
98
- return {
99
- name: k,
100
- pid: v.subprocess.pid,
101
- startTime: v.startTime,
102
- };
103
- }),
104
- }),
105
- );
106
- };
107
- notifySubProcesses();
108
- conn.on('close', () => {
109
- devopsConn = null;
110
- });
111
- conn.on('message', (data) => {
112
- let { command, options } = JSON.parse(data);
113
- console.log('devops cmd', command, options);
114
-
115
- let subprocess;
116
- if (command == 'buildIOS') {
117
- let withEnv = {
118
- withPxKitSource: rootDir.replace(/\\/g, '/'),
119
- };
120
- if (options.rebuild) {
121
- withEnv['rebuild'] = 1;
122
- }
123
- subprocess = cp.spawn('node', [dirConfig.pxembed + '/src/script/itest.js'], {
124
- cwd: dirConfig.pxembed,
125
- detached: true,
126
- env: { ...process.env, ...withEnv },
127
- });
128
- }
129
- else if (command == 'buildAndroid') {
130
- let withEnv = { redirectLogcat: 1 };
131
- let tasks = [];
132
- if (options.java) withEnv['JAVA_HOME'] = options.java;
133
- if (options.rebuild) {
134
- tasks.push('clean');
135
- }
136
- tasks.push('test');
137
- let clfile = dirConfig.pxembed + '/project/android_app/settings.local';
138
- let targetLine = `gradle.ext.withPxKitSource = '${rootDir.replace(/\\/g, '/')}';`;
139
- updateFile(clfile, targetLine);
140
- subprocess = cp.spawn('gradlew' + (runInWindows ? '.bat' : ''), tasks, {
141
- cwd: dirConfig.pxembed + '/project/android_app',
142
- detached: !runInWindows,
143
- windowsHide: true,
144
- shell: true,
145
- env: { ...process.env, ...withEnv },
146
- });
147
- }
148
- else if (command == 'buildHarmony') {
149
- let withEnv = {};
150
- if (options.java) {
151
- withEnv['JAVA_HOME'] = options.java;
152
- withEnv['PATH'] = options.java + '/bin;' + process.env.PATH;
153
- }
154
- if (options.rebuild) {
155
- withEnv['rebuild'] = 1;
156
- }
157
- let clfile = dirConfig.pxembed + '/project/hm_app/hvigorlocal.ts';
158
- let targetLine = `export let withPxKit = '${rootDir.replace(/\\/g, '/')}';`;
159
- updateFile(clfile, targetLine);
160
- subprocess = cp.spawn('node', [dirConfig.pxembed + '/src/script/htest.js', '-b', '-i', '-t'], {
161
- cwd: dirConfig.pxembed,
162
- detached: !runInWindows,
163
- shell: true,
164
- windowsHide: true,
165
- env: { ...process.env, ...withEnv },
166
- });
167
- }
168
- else if (command == 'buildMac') {
169
- let cfg = `--CFG=${options.debug ? 'Debug' : 'Release'}`;
170
- let clean = `${options.rebuild ? '--clean' : ''}`;
171
- let clfile = dirConfig.pxapp + '/cmake.local';
172
- let targetLine = `set(WITH_PX_SOURCE ${rootDir.replace(/\\/g, '/')})`;
173
- updateFile(clfile, targetLine);
174
- subprocess = cp.spawn('jtx', `pxide-build --mac --build ${cfg} ${clean} --test-pxkit`.split(' '), {
175
- cwd: dirConfig.pxapp,
176
- detached: true,
177
- env: { ...process.env, echo: 1, xPX_TEST_FORCE_ALL: 1 },
178
- });
179
- }
180
- else if (command == 'buildWin') {
181
- let cfg = `--CFG=${options.debug ? 'Debug' : 'Release'}`;
182
- let clean = `${options.rebuild ? '--clean' : ''}`;
183
- let clfile = dirConfig.pxapp + '/cmake.local';
184
- let targetLine = `set(WITH_PX_SOURCE ${rootDir.replace(/\\/g, '/')})`;
185
- updateFile(clfile, targetLine);
186
- subprocess = cp.spawn('jtx' + (runInWindows ? '.cmd' : ''), `pxide-build --win --build ${cfg} ${clean} --test-pxkit`.split(' '), {
187
- cwd: dirConfig.pxapp,
188
- detached: !runInWindows,
189
- env: { ...process.env, xPX_TEST_FORCE_ALL: 1 },
190
- windowsHide: true,
191
- shell: true,
192
- });
193
- }
194
- else if (command == 'buildWinMinGW') {
195
- subprocess = cp.spawn('jtx', `build --mingw --build ${options.debug ? '--debug' : '--release'}`.split(' '), {
196
- detached: !runInWindows,
197
- env: { ...process.env, xPX_TEST_FORCE_ALL: 1 },
198
- windowsHide: true,
199
- shell: true,
200
- });
201
- }
202
- else if (command == 'setDir') {
203
- let { id, value } = options;
204
- try {
205
- if (id == 'pxkit') {
206
- value = rootDir;
207
- }
208
- if (value && id in { pxkit: 1, pxembed: 1, pxapp: 1 }) {
209
- let br = cp.execSync('git branch --show-current', { stdio: 'pipe', cwd: value, windowsHide: true });
210
- devopsConn?.send(JSON.stringify({ type: 'setDirResult', id, value, result: br.toString() }));
211
- }
212
- else if (id in { java: 1 }) {
213
- // let ver = cp.spawnSync('java', ['-version'], { stdio: 'pipe' }).stderr.toString().split('\n')[0];
214
- // console.log(value, ver);
215
- // devopsConn?.send(JSON.stringify({ type: 'setDirResult', id, value, result: ver }));
216
- }
217
- dirConfig[id] = value;
218
- }
219
- catch (e) {
220
- devopsConn?.send(JSON.stringify({ type: 'setDirResult', id, result: e.message }));
221
- }
222
- }
223
-
224
- if (subprocess) {
225
- subProcesses[command] = { subprocess, startTime: Date.now() };
226
- subprocess.on('error', (err) => {
227
- devopsConn?.send(JSON.stringify({ type: 'return', name: command, value: -1, err: err.message }));
228
- delete subProcesses[command];
229
- });
230
- subprocess.on('exit', (code, signal) => {
231
- devopsConn?.send(JSON.stringify({ type: 'return', name: command, value: code }));
232
- delete subProcesses[command];
233
- });
234
- subprocess.stdout.on('data', (data) => {
235
- // console.log(data.toString());
236
- devopsConn?.send(JSON.stringify({ type: 'stdout', data: data.toString() }));
237
- });
238
- subprocess.stderr.on('data', (data) => {
239
- // console.log(data.toString());
240
- devopsConn?.send(JSON.stringify({ type: 'stderr', data: data.toString() }));
241
- });
242
- notifySubProcesses();
243
- }
244
- else {
245
- // devopsConn?.send(JSON.stringify({ type: 'return', name: command, value: -1, err: 'unknown command' }));
246
- }
247
- });
248
- }
249
- });
250
-
251
- let tCapDir = rootDir + '/.cache/devops/caps';
252
- if (!fs.existsSync(tCapDir)) fs.mkdirSync(tCapDir, { recursive: true });
253
- var storage = multer.diskStorage({
254
- destination: (req, file, cb) => {
255
- let dir = tCapDir + '/' + req.body.platform;
256
- if (!fs.existsSync(dir)) fs.mkdirSync(dir);
257
- cb(null, dir);
258
- },
259
- filename: (req, file, cb) => cb(null, file.originalname),
260
- });
261
- app.use(express.urlencoded({ extended: true }));
262
- app.post('/compareImage', multer({ storage }).single('file'), (req, res) => {
263
- console.log('Uploaded file:', req.file, req.files);
264
- console.log('body:', req.body);
265
-
266
- if (!req.file) {
267
- return res.status(400).send('No files were uploaded.');
268
- }
269
- let { platform, threshold, includeAA } = req.body;
270
- if (!platform) {
271
- return res.status(400).send('No platform set.');
272
- }
273
- threshold = parseFloat(threshold) || 0;
274
- includeAA = includeAA != 'false';
275
-
276
- const PNG = require('pngjs').PNG;
277
- const pixelmatch = require('pixelmatch');
278
-
279
- let baseFile = `${req.file.path}`.replace('.png', '.base.png');
280
- fs.copyFileSync(`${dirConfig.pxembed}/project/${platform}_app/caps/${req.file.originalname}`, baseFile);
281
-
282
- const img1 = PNG.sync.read(fs.readFileSync(req.file.path));
283
- const img2 = PNG.sync.read(fs.readFileSync(baseFile));
284
- const { width, height } = img1;
285
- const diff = new PNG({ width, height });
286
-
287
- let num = pixelmatch(img1.data, img2.data, diff.data, width, height, { threshold, includeAA });
288
- let diffFile = `${req.file.path}`.replace('.png', '.diff.png');
289
- console.log('compareImage', 'diffNum', num, 'threshold', threshold, req.file.path, baseFile);
290
- if (num) {
291
- fs.writeFileSync(diffFile, PNG.sync.write(diff));
292
- }
293
- else {
294
- if (fs.existsSync(diffFile)) fs.unlinkSync(diffFile);
295
- if (fs.existsSync(baseFile)) fs.unlinkSync(baseFile);
296
- }
297
- res.setHeader('Content-Type', 'application/json');
298
- res.send(JSON.stringify({ diff: num }));
299
- });
300
-
301
- app.use('/proxy/', async (req, res, next) => {
302
- let url = req.url.substring(1).replace('@@@', '://');
303
- console.log('req proxy', url);
304
- try {
305
- const response = await fetch(url);
306
- let data = await response.buffer();
307
- let headers = Array.from(response.headers.entries());
308
- headers.forEach(([k, v]) => {
309
- res.setHeader(k, v);
310
- });
311
- console.log('req proxy', url, data.byteLength);
312
- // res.setHeader('Content-Type', response.headers['Content-Type']);
313
- res.send(data);
314
- }
315
- catch (error) {
316
- console.log('req proxy failed', url, error);
317
- res.sendStatus(500);
318
- }
319
- });
320
-
321
- let mapPort2Process = {};
322
- app.use('/kill_server', async (req, res, next) => {
323
- let url = new URL(req.url, 'http://localhost');
324
- let port = url.searchParams.get('port');
325
- let child = mapPort2Process[port];
326
- console.log('kill_server', port, child?.pid);
327
- devopsConn?.send(JSON.stringify({ type: 'stdout', data: `杀掉服务进程:${child?.pid}:${port}\n` }));
328
- if (child) {
329
- child.kill();
330
- }
331
- res.end();
332
- });
333
- app.use('/run_server', async (req, res, next) => {
334
- let url = new URL(req.url, 'http://localhost');
335
- let cmd = JSON.parse(url.searchParams.get('cmd'));
336
- let command, args;
337
- if (cmd.node) {
338
- command = 'node';
339
- args = [pr(rootDir, 'pxtest/server', cmd.node)];
340
- if (cmd.args) args = args.concat(cmd.args);
341
- }
342
- let child = cp.spawn(command, args, {
343
- windowsHide: true,
344
- shell: false,
345
- stdio: ['ignore', 'pipe', 'pipe', 'ipc'], //注意这里必须使用pipe,如用inherit会导致子进程莫名终止,可能跟自己的stdout被重定向到文件有关
346
- });
347
- console.log('run_server', req.url, child.pid);
348
- devopsConn?.send(JSON.stringify({ type: 'stdout', data: `拉起服务进程:${child.pid}:${req.url}\n` }));
349
- child.once('close', (code, sig) => {
350
- console.log(`server[${child.pid}] close`, code, sig);
351
- });
352
- child.once('error', (err) => {
353
- devopsConn?.send(JSON.stringify({ type: 'stdout', data: `服务进程错误:${child.pid}:${err.message}\n` }));
354
- console.log(`server[${child.pid}] error`, err);
355
- });
356
- child.once('message', (port) => {
357
- console.log(`server[${child.pid}] message`, port);
358
- devopsConn?.send(JSON.stringify({ type: 'stdout', data: `服务进程报告端口号:${child.pid}:${port}\n` }));
359
- mapPort2Process[port] = child;
360
- res.setHeader('content-type', 'text/plain'); //设置类型,避免被pfbs当作html转换(不设的话不知道哪里默认会给它加个text/html))
361
- res.send(port);
362
- });
363
- child.stdout?.on('data', (data) => {
364
- console.log(`server[${child.pid}] stdout:\n`, data.toString());
365
- });
366
- child.stderr?.on('data', (data) => {
367
- console.log(`server[${child.pid}] stderr:\n`, data.toString());
368
- });
369
- });
370
-
371
- return { notifyAppReload };
372
- };
1
+ let ws = require('ws');
2
+ let fs = require('fs');
3
+ let cp = require('child_process');
4
+ let http = require('http');
5
+ let path = require('path');
6
+ let pr = path.resolve;
7
+
8
+ let multer = require('multer');
9
+ let express = require('express');
10
+
11
+ let rootDir = pr(__dirname, '../');
12
+
13
+ let dirConfig = {
14
+ pxkit: '',
15
+ pxembed: '',
16
+ pxapp: '',
17
+ };
18
+
19
+ function updateFile(file, targetLine) {
20
+ targetLine = '\n' + targetLine;
21
+ fs.existsSync(file) ? fs.readFileSync(file, 'utf8').includes(targetLine) || fs.appendFileSync(file, targetLine) : fs.writeFileSync(file, targetLine);
22
+ }
23
+
24
+ module.exports.setupDevops = async function (server, app) {
25
+ let si = require('systeminformation');
26
+ let sys = await si.system();
27
+ let runInVM = /Parallels/.test(sys.manufacturer);
28
+ console.log(`system manufacturer:${sys.manufacturer}, runInVM:${runInVM}`);
29
+
30
+ let watcher;
31
+
32
+ if (!runInVM) {
33
+ let chokidar = require('chokidar');
34
+ watcher = chokidar.watch(
35
+ ['/**/*.html', '/**/*.js'].map((v) => pr('.') + v),
36
+ { ignored: [/\.build\//, /node_modules/], persistent: true },
37
+ );
38
+
39
+ watcher
40
+ .on('change', function (path) {
41
+ console.log('changetest', path);
42
+ })
43
+ .on('error', function (error) {
44
+ console.error('Error happened', error);
45
+ });
46
+ }
47
+
48
+ {
49
+ // let pp = cp.execSync('yarn', { cwd: rootDir + '/pxtest/server/', stdio: 'pipe' });
50
+ // console.log('yarn for pxtest server:\n', pp.toString());
51
+ }
52
+
53
+ function notifyAppReload(htmlPath) {
54
+ if (!watcher) return;
55
+ watcher.emit('change', htmlPath);
56
+ }
57
+
58
+ let subProcesses = {};
59
+ let devopsConn;
60
+ let websocketServer = new ws.Server({ server });
61
+ websocketServer.on('connection', (conn, req) => {
62
+ let url = new URL(req.url, 'http://localhost');
63
+ //去掉第1个字符/,否则在windows上与path比较时会因为\而不一样
64
+ let referrer = url.searchParams.get('referrer')?.substring(1);
65
+ console.log('ws connection', url.pathname, referrer);
66
+ // console.log(url.pathname, url.searchParams);
67
+ if (url.pathname == '/notify') {
68
+ conn.on('message', (message) => {
69
+ // client.send(`{ "message" : ${message} }`);
70
+ });
71
+ let changeHandler;
72
+ watcher?.on(
73
+ 'change',
74
+ (changeHandler = (path) => {
75
+ //只响应该页面对应的文件变化
76
+ console.log('filechange check', path, referrer);
77
+ if (
78
+ path.endsWith(referrer) || //这种情况是静态html文件,只比较尾部相等
79
+ referrer.startsWith(path) //这种情况是webpack里的app,排除用来作route的后段,只比较首段相等
80
+ ) {
81
+ console.log('reload', path);
82
+ conn.send('reload');
83
+ }
84
+ }),
85
+ );
86
+ conn.on('close', () => {
87
+ watcher?.removeListener('change', changeHandler);
88
+ console.log('ws connection closed', url.pathname, url.searchParams);
89
+ });
90
+ } else if (url.pathname == '/devops') {
91
+ devopsConn = conn;
92
+ let notifySubProcesses = () => {
93
+ conn.send(
94
+ JSON.stringify({
95
+ type: 'subProcesses',
96
+ data: Object.entries(subProcesses).map(([k, v]) => {
97
+ return {
98
+ name: k,
99
+ pid: v.subprocess.pid,
100
+ startTime: v.startTime,
101
+ };
102
+ }),
103
+ }),
104
+ );
105
+ };
106
+ notifySubProcesses();
107
+ conn.on('close', () => {
108
+ devopsConn = null;
109
+ });
110
+ conn.on('message', (data) => {
111
+ let { command, options } = JSON.parse(data);
112
+ console.log('devops cmd', command, options);
113
+
114
+ let subprocess;
115
+ if (command == 'buildIOS') {
116
+ let withEnv = {
117
+ withPxKitSource: rootDir.replace(/\\/g, '/'),
118
+ };
119
+ if (options.rebuild) {
120
+ withEnv['rebuild'] = 1;
121
+ }
122
+ subprocess = cp.spawn('node', [dirConfig.pxembed + '/src/script/itest.js'], {
123
+ cwd: dirConfig.pxembed,
124
+ detached: true,
125
+ env: { ...process.env, ...withEnv },
126
+ });
127
+ } else if (command == 'buildAndroid') {
128
+ let withEnv = { redirectLogcat: 1 };
129
+ let tasks = [];
130
+ if (options.java) withEnv['JAVA_HOME'] = options.java;
131
+ if (options.rebuild) {
132
+ tasks.push('clean');
133
+ }
134
+ tasks.push('test');
135
+ let clfile = dirConfig.pxembed + '/project/android_app/settings.local';
136
+ let targetLine = `gradle.ext.withPxKitSource = '${rootDir.replace(/\\/g, '/')}';`;
137
+ updateFile(clfile, targetLine);
138
+ subprocess = cp.spawn('gradlew' + (runInWindows ? '.bat' : ''), tasks, {
139
+ cwd: dirConfig.pxembed + '/project/android_app',
140
+ detached: !runInWindows,
141
+ windowsHide: true,
142
+ shell: true,
143
+ env: { ...process.env, ...withEnv },
144
+ });
145
+ } else if (command == 'buildHarmony') {
146
+ let withEnv = {};
147
+ if (options.java) {
148
+ withEnv['JAVA_HOME'] = options.java;
149
+ withEnv['PATH'] = options.java + '/bin;' + process.env.PATH;
150
+ }
151
+ if (options.rebuild) {
152
+ withEnv['rebuild'] = 1;
153
+ }
154
+ let clfile = dirConfig.pxembed + '/project/hm_app/hvigorlocal.ts';
155
+ let targetLine = `export let withPxKit = '${rootDir.replace(/\\/g, '/')}';`;
156
+ updateFile(clfile, targetLine);
157
+ subprocess = cp.spawn('node', [dirConfig.pxembed + '/src/script/htest.js', '-b', '-i', '-t'], {
158
+ cwd: dirConfig.pxembed,
159
+ detached: !runInWindows,
160
+ shell: true,
161
+ windowsHide: true,
162
+ env: { ...process.env, ...withEnv },
163
+ });
164
+ } else if (command == 'buildMac') {
165
+ let cfg = `--CFG=${options.debug ? 'Debug' : 'Release'}`;
166
+ let clean = `${options.rebuild ? '--clean' : ''}`;
167
+ let clfile = dirConfig.pxapp + '/cmake.local';
168
+ let targetLine = `set(WITH_PX_SOURCE ${rootDir.replace(/\\/g, '/')})`;
169
+ updateFile(clfile, targetLine);
170
+ subprocess = cp.spawn('jtx', `pxide-build --mac --build ${cfg} ${clean} --test-pxkit`.split(' '), {
171
+ cwd: dirConfig.pxapp,
172
+ detached: true,
173
+ env: { ...process.env, echo: 1, xPX_TEST_FORCE_ALL: 1 },
174
+ });
175
+ } else if (command == 'buildWin') {
176
+ let cfg = `--CFG=${options.debug ? 'Debug' : 'Release'}`;
177
+ let clean = `${options.rebuild ? '--clean' : ''}`;
178
+ let clfile = dirConfig.pxapp + '/cmake.local';
179
+ let targetLine = `set(WITH_PX_SOURCE ${rootDir.replace(/\\/g, '/')})`;
180
+ updateFile(clfile, targetLine);
181
+ subprocess = cp.spawn('jtx' + (runInWindows ? '.cmd' : ''), `pxide-build --win --build ${cfg} ${clean} --test-pxkit`.split(' '), {
182
+ cwd: dirConfig.pxapp,
183
+ detached: !runInWindows,
184
+ env: { ...process.env, xPX_TEST_FORCE_ALL: 1 },
185
+ windowsHide: true,
186
+ shell: true,
187
+ });
188
+ } else if (command == 'buildWinMinGW') {
189
+ subprocess = cp.spawn('jtx', `build --mingw --build ${options.debug ? '--debug' : '--release'}`.split(' '), {
190
+ detached: !runInWindows,
191
+ env: { ...process.env, xPX_TEST_FORCE_ALL: 1 },
192
+ windowsHide: true,
193
+ shell: true,
194
+ });
195
+ } else if (command == 'setDir') {
196
+ let { id, value } = options;
197
+ try {
198
+ if (id == 'pxkit') {
199
+ value = rootDir;
200
+ }
201
+ if (value && id in { pxkit: 1, pxembed: 1, pxapp: 1 }) {
202
+ let br = cp.execSync('git branch --show-current', { stdio: 'pipe', cwd: value, windowsHide: true });
203
+ devopsConn?.send(JSON.stringify({ type: 'setDirResult', id, value, result: br.toString() }));
204
+ } else if (id in { java: 1 }) {
205
+ // let ver = cp.spawnSync('java', ['-version'], { stdio: 'pipe' }).stderr.toString().split('\n')[0];
206
+ // console.log(value, ver);
207
+ // devopsConn?.send(JSON.stringify({ type: 'setDirResult', id, value, result: ver }));
208
+ }
209
+ dirConfig[id] = value;
210
+ } catch (e) {
211
+ devopsConn?.send(JSON.stringify({ type: 'setDirResult', id, result: e.message }));
212
+ }
213
+ }
214
+
215
+ if (subprocess) {
216
+ subProcesses[command] = { subprocess, startTime: Date.now() };
217
+ subprocess.on('error', (err) => {
218
+ devopsConn?.send(JSON.stringify({ type: 'return', name: command, value: -1, err: err.message }));
219
+ delete subProcesses[command];
220
+ });
221
+ subprocess.on('exit', (code, signal) => {
222
+ devopsConn?.send(JSON.stringify({ type: 'return', name: command, value: code }));
223
+ delete subProcesses[command];
224
+ });
225
+ subprocess.stdout.on('data', (data) => {
226
+ // console.log(data.toString());
227
+ devopsConn?.send(JSON.stringify({ type: 'stdout', data: data.toString() }));
228
+ });
229
+ subprocess.stderr.on('data', (data) => {
230
+ // console.log(data.toString());
231
+ devopsConn?.send(JSON.stringify({ type: 'stderr', data: data.toString() }));
232
+ });
233
+ notifySubProcesses();
234
+ } else {
235
+ // devopsConn?.send(JSON.stringify({ type: 'return', name: command, value: -1, err: 'unknown command' }));
236
+ }
237
+ });
238
+ }
239
+ });
240
+
241
+ let tCapDir = rootDir + '/.cache/devops/caps';
242
+ if (!fs.existsSync(tCapDir)) fs.mkdirSync(tCapDir, { recursive: true });
243
+ var storage = multer.diskStorage({
244
+ destination: (req, file, cb) => {
245
+ let dir = tCapDir + '/' + req.body.platform;
246
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir);
247
+ cb(null, dir);
248
+ },
249
+ filename: (req, file, cb) => cb(null, file.originalname),
250
+ });
251
+ app.use(express.urlencoded({ extended: true }));
252
+ app.post('/compareImage', multer({ storage }).single('file'), (req, res) => {
253
+ console.log('Uploaded file:', req.file, req.files);
254
+ console.log('body:', req.body);
255
+
256
+ if (!req.file) {
257
+ return res.status(400).send('No files were uploaded.');
258
+ }
259
+ let { platform, threshold, includeAA } = req.body;
260
+ if (!platform) {
261
+ return res.status(400).send('No platform set.');
262
+ }
263
+ threshold = parseFloat(threshold) || 0;
264
+ includeAA = includeAA != 'false';
265
+
266
+ const PNG = require('pngjs').PNG;
267
+ const pixelmatch = require('pixelmatch');
268
+
269
+ let baseFile = `${req.file.path}`.replace('.png', '.base.png');
270
+ fs.copyFileSync(`${dirConfig.pxembed}/project/${platform}_app/caps/${req.file.originalname}`, baseFile);
271
+
272
+ const img1 = PNG.sync.read(fs.readFileSync(req.file.path));
273
+ const img2 = PNG.sync.read(fs.readFileSync(baseFile));
274
+ const { width, height } = img1;
275
+ const diff = new PNG({ width, height });
276
+
277
+ let num = pixelmatch(img1.data, img2.data, diff.data, width, height, { threshold, includeAA });
278
+ let diffFile = `${req.file.path}`.replace('.png', '.diff.png');
279
+ console.log('compareImage', 'diffNum', num, 'threshold', threshold, req.file.path, baseFile);
280
+ if (num) {
281
+ fs.writeFileSync(diffFile, PNG.sync.write(diff));
282
+ } else {
283
+ if (fs.existsSync(diffFile)) fs.unlinkSync(diffFile);
284
+ if (fs.existsSync(baseFile)) fs.unlinkSync(baseFile);
285
+ }
286
+ res.setHeader('Content-Type', 'application/json');
287
+ res.send(JSON.stringify({ diff: num }));
288
+ });
289
+
290
+ app.use('/proxy/', async (req, res, next) => {
291
+ let url = req.url.substring(1).replace('@@@', '://');
292
+ console.log('req proxy', url);
293
+ try {
294
+ const response = await fetch(url);
295
+ let data = await response.buffer();
296
+ let headers = Array.from(response.headers.entries());
297
+ headers.forEach(([k, v]) => {
298
+ res.setHeader(k, v);
299
+ });
300
+ console.log('req proxy', url, data.byteLength);
301
+ // res.setHeader('Content-Type', response.headers['Content-Type']);
302
+ res.send(data);
303
+ } catch (error) {
304
+ console.log('req proxy failed', url, error);
305
+ res.sendStatus(500);
306
+ }
307
+ });
308
+
309
+ let mapPort2Process = {};
310
+ app.use('/kill_server', async (req, res, next) => {
311
+ let url = new URL(req.url, 'http://localhost');
312
+ let port = url.searchParams.get('port');
313
+ let child = mapPort2Process[port];
314
+ console.log('kill_server', port, child?.pid);
315
+ devopsConn?.send(JSON.stringify({ type: 'stdout', data: `杀掉服务进程:${child?.pid}:${port}\n` }));
316
+ if (child) {
317
+ child.kill();
318
+ }
319
+ res.end();
320
+ });
321
+ app.use('/run_server', async (req, res, next) => {
322
+ let url = new URL(req.url, 'http://localhost');
323
+ let cmd = JSON.parse(url.searchParams.get('cmd'));
324
+ let command, args;
325
+ if (cmd.node) {
326
+ command = 'node';
327
+ args = [pr(rootDir, 'pxtest/server', cmd.node)];
328
+ if (cmd.args) args = args.concat(cmd.args);
329
+ }
330
+ let child = cp.spawn(command, args, {
331
+ windowsHide: true,
332
+ shell: false,
333
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'], //注意这里必须使用pipe,如用inherit会导致子进程莫名终止,可能跟自己的stdout被重定向到文件有关
334
+ });
335
+ console.log('run_server', req.url, child.pid);
336
+ devopsConn?.send(JSON.stringify({ type: 'stdout', data: `拉起服务进程:${child.pid}:${req.url}\n` }));
337
+ child.once('close', (code, sig) => {
338
+ console.log(`server[${child.pid}] close`, code, sig);
339
+ });
340
+ child.once('error', (err) => {
341
+ devopsConn?.send(JSON.stringify({ type: 'stdout', data: `服务进程错误:${child.pid}:${err.message}\n` }));
342
+ console.log(`server[${child.pid}] error`, err);
343
+ });
344
+ child.once('message', (port) => {
345
+ console.log(`server[${child.pid}] message`, port);
346
+ devopsConn?.send(JSON.stringify({ type: 'stdout', data: `服务进程报告端口号:${child.pid}:${port}\n` }));
347
+ mapPort2Process[port] = child;
348
+ res.setHeader('content-type', 'text/plain'); //设置类型,避免被pfbs当作html转换(不设的话不知道哪里默认会给它加个text/html))
349
+ res.send(port);
350
+ });
351
+ child.stdout?.on('data', (data) => {
352
+ console.log(`server[${child.pid}] stdout:\n`, data.toString());
353
+ });
354
+ child.stderr?.on('data', (data) => {
355
+ console.log(`server[${child.pid}] stderr:\n`, data.toString());
356
+ });
357
+ });
358
+
359
+ return { notifyAppReload };
360
+ };