@maoyugames/phaser-framework 1.0.1 → 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/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +321 -108
- package/dist/index.js +44 -35
- package/package.json +6 -2
package/dist/cli/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* pf build <platform|all> 构建某平台/全部平台(临时入口 + 编程式 vite + 外壳注入 + 校验)
|
|
8
8
|
* pf size-check [platform] 产物体积检查(扫 <root>/dist/<p>)
|
|
9
9
|
* pf verify-isolation [platform] SDK 隔离校验(扫 <root>/dist/<p>)
|
|
10
|
+
* pf verify [platform] [--no-build] 实跑冒烟验证(Playwright 无头:确认渲染 + 无运行期错误)
|
|
10
11
|
* pf cap <sync|open> [android|ios] Capacitor 同步/打开原生工程
|
|
11
12
|
* pf help 查看帮助
|
|
12
13
|
*
|
package/dist/cli/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import '../chunk-PKBMQBKP.js';
|
|
3
|
-
import
|
|
4
|
-
import { resolve, join, relative, basename, dirname } from 'path';
|
|
3
|
+
import pc6 from 'picocolors';
|
|
4
|
+
import { resolve, join, extname, relative, basename, dirname } from 'path';
|
|
5
5
|
import { randomBytes } from 'crypto';
|
|
6
6
|
import fse4 from 'fs-extra';
|
|
7
7
|
import { createRequire } from 'module';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
-
import { existsSync, readFileSync } from 'fs';
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync, readFile, readFileSync } from 'fs';
|
|
10
|
+
import http from 'http';
|
|
10
11
|
import { spawnSync } from 'child_process';
|
|
11
12
|
|
|
12
13
|
// src/cli/platforms-meta.ts
|
|
@@ -236,7 +237,15 @@ function createPlatformConfig(platform, opts) {
|
|
|
236
237
|
__FRAMEWORK_VERSION__: JSON.stringify(frameworkVersion)
|
|
237
238
|
},
|
|
238
239
|
resolve: {
|
|
239
|
-
alias: createAlias(projectRoot)
|
|
240
|
+
alias: createAlias(projectRoot),
|
|
241
|
+
// 去重 phaser:避免框架包与业务解析到不同 phaser 实例
|
|
242
|
+
dedupe: ["phaser"]
|
|
243
|
+
},
|
|
244
|
+
// dev 模式必须预打包 phaser(及框架包)为带 default 导出的 ESM,
|
|
245
|
+
// 否则 vite dev 下 `import Phaser from 'phaser'` 报
|
|
246
|
+
// "phaser does not provide an export named 'default'"(build 模式 rollup 自带 CJS 互操作,不受影响)。
|
|
247
|
+
optimizeDeps: {
|
|
248
|
+
include: ["phaser", "@maoyugames/phaser-framework"]
|
|
240
249
|
},
|
|
241
250
|
esbuild: {
|
|
242
251
|
// 兼容低端 webview / 小游戏 JS 引擎
|
|
@@ -530,7 +539,7 @@ function writeShellFiles(distDir, projectRoot, files) {
|
|
|
530
539
|
const dst = join(distDir, f.relPath);
|
|
531
540
|
fse4.ensureDirSync(resolve(dst, ".."));
|
|
532
541
|
fse4.writeFileSync(dst, f.content, "utf-8");
|
|
533
|
-
console.log(
|
|
542
|
+
console.log(pc6.green(` \u2713 ${f.relPath} \u2192 ${relative(projectRoot, dst)}`));
|
|
534
543
|
}
|
|
535
544
|
}
|
|
536
545
|
function injectShell(opts) {
|
|
@@ -540,19 +549,19 @@ function injectShell(opts) {
|
|
|
540
549
|
case "tiktok":
|
|
541
550
|
return true;
|
|
542
551
|
case "facebook": {
|
|
543
|
-
console.log(
|
|
552
|
+
console.log(pc6.cyan(` \u25B6 [facebook] \u6CE8\u5165\u5916\u58F3\u6587\u4EF6`));
|
|
544
553
|
writeShellFiles(distDir, projectRoot, [renderFacebookAppConfig(config.facebook)]);
|
|
545
554
|
return true;
|
|
546
555
|
}
|
|
547
556
|
case "capacitor": {
|
|
548
|
-
console.log(
|
|
557
|
+
console.log(pc6.cyan(` \u25B6 [capacitor] \u751F\u6210 capacitor.config.ts(\u9879\u76EE\u6839)`));
|
|
549
558
|
const capCfgPath = resolve(projectRoot, "capacitor.config.ts");
|
|
550
559
|
fse4.writeFileSync(capCfgPath, renderCapacitorConfig(config.capacitor), "utf-8");
|
|
551
|
-
console.log(
|
|
560
|
+
console.log(pc6.green(` \u2713 capacitor.config.ts \u2192 ${relative(projectRoot, capCfgPath)}`));
|
|
552
561
|
return true;
|
|
553
562
|
}
|
|
554
563
|
case "wechat": {
|
|
555
|
-
console.log(
|
|
564
|
+
console.log(pc6.cyan(` \u25B6 [wechat] \u6CE8\u5165\u5916\u58F3\u6587\u4EF6`));
|
|
556
565
|
writeShellFiles(distDir, projectRoot, renderWeChatShells(config.wechat));
|
|
557
566
|
copyPublicAssetsToWechat(projectRoot, distDir);
|
|
558
567
|
copyWechatVendor(projectRoot, distDir);
|
|
@@ -565,37 +574,37 @@ function injectShell(opts) {
|
|
|
565
574
|
function copyPublicAssetsToWechat(projectRoot, distDir) {
|
|
566
575
|
const srcPublic = resolve(projectRoot, "src/game/public");
|
|
567
576
|
if (!fse4.existsSync(srcPublic)) return;
|
|
568
|
-
console.log(
|
|
577
|
+
console.log(pc6.cyan(" \u25B6 [wechat] \u62F7\u8D1D\u4E1A\u52A1\u9759\u6001\u8D44\u6E90(src/game/public)"));
|
|
569
578
|
fse4.copySync(srcPublic, distDir);
|
|
570
|
-
console.log(
|
|
579
|
+
console.log(pc6.green(" \u2713 src/game/public/* \u2192 dist/wechat/"));
|
|
571
580
|
}
|
|
572
581
|
function copyWechatVendor(projectRoot, distDir) {
|
|
573
|
-
console.log(
|
|
582
|
+
console.log(pc6.cyan(" \u25B6 [wechat] \u68C0\u6D4B\u5FAE\u4FE1\u9002\u914D\u5668(vendor)"));
|
|
574
583
|
const dirs = wechatVendorDirs(projectRoot);
|
|
575
584
|
const adapter = dirs.map((d) => join(d, "weapp-adapter.js")).find((p) => fse4.existsSync(p));
|
|
576
585
|
if (adapter) {
|
|
577
586
|
fse4.copyFileSync(adapter, join(distDir, "weapp-adapter.js"));
|
|
578
|
-
console.log(
|
|
587
|
+
console.log(pc6.green(" \u2713 weapp-adapter.js \u2192 dist/wechat/weapp-adapter.js"));
|
|
579
588
|
const vendorDir = resolve(adapter, "..");
|
|
580
589
|
const symbol = join(vendorDir, "symbol.js");
|
|
581
590
|
if (fse4.existsSync(symbol)) {
|
|
582
591
|
fse4.copyFileSync(symbol, join(distDir, "symbol.js"));
|
|
583
|
-
console.log(
|
|
592
|
+
console.log(pc6.green(" \u2713 symbol.js \u2192 dist/wechat/symbol.js"));
|
|
584
593
|
}
|
|
585
594
|
return true;
|
|
586
595
|
}
|
|
587
|
-
console.log(
|
|
596
|
+
console.log(pc6.yellow(pc6.bold(" \u26A0 \u7F3A\u5C11\u5FAE\u4FE1\u9002\u914D\u5668 weapp-adapter.js")));
|
|
588
597
|
console.log(
|
|
589
|
-
|
|
598
|
+
pc6.yellow(
|
|
590
599
|
" \u5FAE\u4FE1\u5C0F\u6E38\u620F\u65E0 DOM,\u9700\u6B64\u5B98\u65B9\u9002\u914D\u5668\u6CE8\u5165 canvas/document shim,\u5426\u5219 game.js \u542F\u52A8\u5373\u62A5\u9519\u3002"
|
|
591
600
|
)
|
|
592
601
|
);
|
|
593
|
-
console.log(
|
|
602
|
+
console.log(pc6.yellow(` \u8BF7\u628A\u5B98\u65B9 weapp-adapter.js(\u53CA\u53EF\u9009 symbol.js)\u653E\u5230\u4EE5\u4E0B\u4EFB\u4E00\u76EE\u5F55\u540E\u91CD\u65B0\u6784\u5EFA:`));
|
|
594
603
|
for (const d of wechatVendorDirs(projectRoot)) {
|
|
595
|
-
console.log(
|
|
604
|
+
console.log(pc6.yellow(` - ${relative(projectRoot, d)}/weapp-adapter.js`));
|
|
596
605
|
}
|
|
597
606
|
console.log(
|
|
598
|
-
|
|
607
|
+
pc6.yellow(" \u83B7\u53D6\u65B9\u5F0F:\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u65B0\u5EFA\u300C\u5C0F\u6E38\u620F\u300D\u9879\u76EE\u6A21\u677F\u91CC\u9644\u5E26 weapp-adapter.js\u3002")
|
|
599
608
|
);
|
|
600
609
|
return false;
|
|
601
610
|
}
|
|
@@ -619,43 +628,43 @@ function findMainBundle(files) {
|
|
|
619
628
|
}
|
|
620
629
|
function checkPlatform(platform, distDir) {
|
|
621
630
|
if (!fse4.existsSync(distDir)) {
|
|
622
|
-
console.log(
|
|
631
|
+
console.log(pc6.gray(` [${platform}] \u672A\u6784\u5EFA,\u8DF3\u8FC7`));
|
|
623
632
|
return true;
|
|
624
633
|
}
|
|
625
634
|
const files = listFiles(distDir);
|
|
626
635
|
const total = files.reduce((s, f) => s + f.size, 0);
|
|
627
636
|
const main2 = findMainBundle(files);
|
|
628
637
|
const limits = SIZE_LIMITS[platform];
|
|
629
|
-
console.log(
|
|
630
|
-
console.log(` \u603B\u4F53\u79EF:${
|
|
638
|
+
console.log(pc6.cyan(` [${platform}]`));
|
|
639
|
+
console.log(` \u603B\u4F53\u79EF:${pc6.bold(formatBytes(total))}(${files.length} \u4E2A\u6587\u4EF6)`);
|
|
631
640
|
if (main2) {
|
|
632
|
-
console.log(` \u4E3B bundle:${basename(main2.path)} = ${
|
|
641
|
+
console.log(` \u4E3B bundle:${basename(main2.path)} = ${pc6.bold(formatBytes(main2.size))}`);
|
|
633
642
|
}
|
|
634
643
|
let ok = true;
|
|
635
644
|
if (limits?.totalHardLimit) {
|
|
636
645
|
if (total > limits.totalHardLimit) {
|
|
637
646
|
console.log(
|
|
638
|
-
|
|
647
|
+
pc6.red(
|
|
639
648
|
` \u2717 \u8D85\u51FA TikTok \u6574\u5305\u786C\u4E0A\u9650 ${formatBytes(limits.totalHardLimit)}(\u8D85 ${formatBytes(total - limits.totalHardLimit)})`
|
|
640
649
|
)
|
|
641
650
|
);
|
|
642
651
|
ok = false;
|
|
643
652
|
} else {
|
|
644
653
|
console.log(
|
|
645
|
-
|
|
654
|
+
pc6.green(` \u2713 \u5728 TikTok 50MB \u6574\u5305\u4E0A\u9650\u5185(\u4F59 ${formatBytes(limits.totalHardLimit - total)})`)
|
|
646
655
|
);
|
|
647
656
|
}
|
|
648
657
|
}
|
|
649
658
|
if (limits?.mainBundleSoftLimit && main2) {
|
|
650
659
|
if (main2.size > limits.mainBundleSoftLimit) {
|
|
651
660
|
console.log(
|
|
652
|
-
|
|
661
|
+
pc6.yellow(
|
|
653
662
|
` \u26A0 \u5FAE\u4FE1\u4E3B\u5305\u8D85\u8FC7\u5EFA\u8BAE ${formatBytes(limits.mainBundleSoftLimit)}(\u5F53\u524D ${formatBytes(main2.size)});\u5EFA\u8BAE\u62C6\u5206\u5305\u6216\u88C1\u526A\u8D44\u6E90,\u4F46\u4E0D\u963B\u65AD\u6784\u5EFA`
|
|
654
663
|
)
|
|
655
664
|
);
|
|
656
665
|
} else {
|
|
657
666
|
console.log(
|
|
658
|
-
|
|
667
|
+
pc6.green(` \u2713 \u5FAE\u4FE1\u4E3B\u5305\u5728 4MB \u5EFA\u8BAE\u503C\u5185(\u4F59 ${formatBytes(limits.mainBundleSoftLimit - main2.size)})`)
|
|
659
668
|
);
|
|
660
669
|
}
|
|
661
670
|
}
|
|
@@ -664,29 +673,29 @@ function checkPlatform(platform, distDir) {
|
|
|
664
673
|
function runSizeCheck(target) {
|
|
665
674
|
const ctx = resolveProject();
|
|
666
675
|
const targets = target ? isPlatform(target) ? [target] : invalidTarget(target) : ALL_PLATFORMS;
|
|
667
|
-
console.log(
|
|
676
|
+
console.log(pc6.cyan(pc6.bold("\u25B6 \u4EA7\u7269\u4F53\u79EF\u68C0\u67E5")));
|
|
668
677
|
let allOk = true;
|
|
669
678
|
for (const p of targets) {
|
|
670
679
|
const distDir = join(ctx.root, "dist", p);
|
|
671
680
|
if (!checkPlatform(p, distDir)) allOk = false;
|
|
672
681
|
}
|
|
673
682
|
if (!allOk) {
|
|
674
|
-
console.log(
|
|
683
|
+
console.log(pc6.red(pc6.bold("\n\u2717 \u4F53\u79EF\u68C0\u67E5\u672A\u901A\u8FC7(\u5B58\u5728\u8D85\u786C\u4E0A\u9650\u7684\u5E73\u53F0)")));
|
|
675
684
|
return false;
|
|
676
685
|
}
|
|
677
|
-
console.log(
|
|
686
|
+
console.log(pc6.green(pc6.bold("\n\u2713 \u4F53\u79EF\u68C0\u67E5\u5B8C\u6210")));
|
|
678
687
|
return true;
|
|
679
688
|
}
|
|
680
689
|
function sizeCheckPlatform(platform, distDir) {
|
|
681
|
-
console.log(
|
|
690
|
+
console.log(pc6.cyan(pc6.bold("\u25B6 \u4EA7\u7269\u4F53\u79EF\u68C0\u67E5")));
|
|
682
691
|
const ok = checkPlatform(platform, distDir);
|
|
683
|
-
if (ok) console.log(
|
|
684
|
-
else console.log(
|
|
692
|
+
if (ok) console.log(pc6.green(pc6.bold("\u2713 \u4F53\u79EF\u68C0\u67E5\u5B8C\u6210")));
|
|
693
|
+
else console.log(pc6.red(pc6.bold("\u2717 \u4F53\u79EF\u68C0\u67E5\u672A\u901A\u8FC7")));
|
|
685
694
|
return ok;
|
|
686
695
|
}
|
|
687
696
|
function invalidTarget(target) {
|
|
688
|
-
console.log(
|
|
689
|
-
console.log(
|
|
697
|
+
console.log(pc6.red(`\u672A\u77E5\u5E73\u53F0:${target}`));
|
|
698
|
+
console.log(pc6.gray(`\u53EF\u9009:${ALL_PLATFORMS.join(" / ")}`));
|
|
690
699
|
process.exit(1);
|
|
691
700
|
}
|
|
692
701
|
function collectJsFiles(dir) {
|
|
@@ -708,12 +717,12 @@ function snippetAround(text, index, span = 60) {
|
|
|
708
717
|
function verifyPlatform(platform, distDir, projectRoot) {
|
|
709
718
|
const rules = ISOLATION_RULES[platform];
|
|
710
719
|
if (!fse4.existsSync(distDir)) {
|
|
711
|
-
console.log(
|
|
720
|
+
console.log(pc6.gray(` [${platform}] \u672A\u6784\u5EFA(${relative(projectRoot, distDir)} \u4E0D\u5B58\u5728),\u8DF3\u8FC7`));
|
|
712
721
|
return 0;
|
|
713
722
|
}
|
|
714
723
|
const files = collectJsFiles(distDir);
|
|
715
724
|
if (files.length === 0) {
|
|
716
|
-
console.log(
|
|
725
|
+
console.log(pc6.yellow(` [${platform}] \u672A\u53D1\u73B0 JS \u4EA7\u7269,\u8DF3\u8FC7`));
|
|
717
726
|
return 0;
|
|
718
727
|
}
|
|
719
728
|
let hits = 0;
|
|
@@ -727,13 +736,13 @@ function verifyPlatform(platform, distDir, projectRoot) {
|
|
|
727
736
|
hits++;
|
|
728
737
|
perSig++;
|
|
729
738
|
const rel = relative(projectRoot, file);
|
|
730
|
-
console.log(
|
|
731
|
-
console.log(
|
|
732
|
-
console.log(
|
|
739
|
+
console.log(pc6.red(` \u2717 [${platform}] \u547D\u4E2D\u7981\u7528 SDK \u7B7E\u540D ${pc6.bold(sig)}`));
|
|
740
|
+
console.log(pc6.gray(` \u6587\u4EF6:${rel}`));
|
|
741
|
+
console.log(pc6.gray(` \u7247\u6BB5:\u2026${snippetAround(text, idx)}\u2026`));
|
|
733
742
|
from = idx + sig.length;
|
|
734
743
|
idx = text.indexOf(sig, from);
|
|
735
744
|
if (perSig >= 3 && idx !== -1) {
|
|
736
|
-
console.log(
|
|
745
|
+
console.log(pc6.gray(` (\u8BE5\u7B7E\u540D\u5728\u672C\u6587\u4EF6\u8FD8\u6709\u66F4\u591A\u547D\u4E2D,\u5DF2\u6298\u53E0)`));
|
|
737
746
|
break;
|
|
738
747
|
}
|
|
739
748
|
}
|
|
@@ -741,7 +750,7 @@ function verifyPlatform(platform, distDir, projectRoot) {
|
|
|
741
750
|
}
|
|
742
751
|
if (hits === 0) {
|
|
743
752
|
console.log(
|
|
744
|
-
|
|
753
|
+
pc6.green(` \u2713 [${platform}] \u9694\u79BB\u901A\u8FC7(\u626B\u63CF ${files.length} \u4E2A JS \u6587\u4EF6,\u65E0\u5176\u5B83\u5E73\u53F0 SDK \u7B7E\u540D)`)
|
|
745
754
|
);
|
|
746
755
|
}
|
|
747
756
|
return hits;
|
|
@@ -749,32 +758,32 @@ function verifyPlatform(platform, distDir, projectRoot) {
|
|
|
749
758
|
function runVerifyIsolation(target) {
|
|
750
759
|
const ctx = resolveProject();
|
|
751
760
|
const targets = target ? isPlatform(target) ? [target] : invalidTarget2(target) : ALL_PLATFORMS;
|
|
752
|
-
console.log(
|
|
761
|
+
console.log(pc6.cyan(pc6.bold("\u25B6 SDK \u9694\u79BB\u6821\u9A8C")));
|
|
753
762
|
let totalHits = 0;
|
|
754
763
|
for (const p of targets) {
|
|
755
764
|
totalHits += verifyPlatform(p, join(ctx.root, "dist", p), ctx.root);
|
|
756
765
|
}
|
|
757
766
|
if (totalHits > 0) {
|
|
758
|
-
console.log(
|
|
767
|
+
console.log(pc6.red(pc6.bold(`
|
|
759
768
|
\u2717 \u9694\u79BB\u6821\u9A8C\u5931\u8D25:\u5171\u53D1\u73B0 ${totalHits} \u5904\u8DE8\u5E73\u53F0 SDK \u7B7E\u540D\u6CC4\u6F0F`)));
|
|
760
769
|
return false;
|
|
761
770
|
}
|
|
762
|
-
console.log(
|
|
771
|
+
console.log(pc6.green(pc6.bold("\n\u2713 \u9694\u79BB\u6821\u9A8C\u5168\u90E8\u901A\u8FC7")));
|
|
763
772
|
return true;
|
|
764
773
|
}
|
|
765
774
|
function verifyIsolationPlatform(platform, distDir, projectRoot) {
|
|
766
|
-
console.log(
|
|
775
|
+
console.log(pc6.cyan(pc6.bold("\u25B6 SDK \u9694\u79BB\u6821\u9A8C")));
|
|
767
776
|
const hits = verifyPlatform(platform, distDir, projectRoot);
|
|
768
777
|
if (hits > 0) {
|
|
769
|
-
console.log(
|
|
778
|
+
console.log(pc6.red(pc6.bold(`\u2717 \u9694\u79BB\u6821\u9A8C\u5931\u8D25:${hits} \u5904\u8DE8\u5E73\u53F0 SDK \u7B7E\u540D\u6CC4\u6F0F`)));
|
|
770
779
|
return false;
|
|
771
780
|
}
|
|
772
|
-
console.log(
|
|
781
|
+
console.log(pc6.green(pc6.bold("\u2713 \u9694\u79BB\u6821\u9A8C\u901A\u8FC7")));
|
|
773
782
|
return true;
|
|
774
783
|
}
|
|
775
784
|
function invalidTarget2(target) {
|
|
776
|
-
console.log(
|
|
777
|
-
console.log(
|
|
785
|
+
console.log(pc6.red(`\u672A\u77E5\u5E73\u53F0:${target}`));
|
|
786
|
+
console.log(pc6.gray(`\u53EF\u9009:${ALL_PLATFORMS.join(" / ")}`));
|
|
778
787
|
process.exit(1);
|
|
779
788
|
}
|
|
780
789
|
|
|
@@ -920,7 +929,7 @@ function findFirstHtml(dir) {
|
|
|
920
929
|
return void 0;
|
|
921
930
|
}
|
|
922
931
|
async function buildPlatform(platform, ctx, config, vite) {
|
|
923
|
-
console.log(
|
|
932
|
+
console.log(pc6.bgCyan(pc6.black(` \u6784\u5EFA\u5E73\u53F0:${platform} `)));
|
|
924
933
|
const cache = cacheDir(ctx.root);
|
|
925
934
|
fse4.ensureDirSync(cache);
|
|
926
935
|
const hash = randomBytes(4).toString("hex");
|
|
@@ -940,7 +949,7 @@ async function buildPlatform(platform, ctx, config, vite) {
|
|
|
940
949
|
tempFiles.push(htmlPath);
|
|
941
950
|
inputFile = htmlPath;
|
|
942
951
|
}
|
|
943
|
-
console.log(
|
|
952
|
+
console.log(pc6.cyan(` \u25B6 [${platform}] vite build`));
|
|
944
953
|
const inlineConfig = createPlatformConfig(platform, {
|
|
945
954
|
projectRoot: ctx.root,
|
|
946
955
|
viteRoot: platform === "wechat" ? ctx.root : cache,
|
|
@@ -953,22 +962,22 @@ async function buildPlatform(platform, ctx, config, vite) {
|
|
|
953
962
|
if (platform !== "wechat") {
|
|
954
963
|
normalizeHtmlOutput(distDir);
|
|
955
964
|
}
|
|
956
|
-
console.log(
|
|
965
|
+
console.log(pc6.green(` \u2713 [${platform}] vite build \u5B8C\u6210 \u2192 ${relative(ctx.root, distDir)}`));
|
|
957
966
|
const injectOk = injectShell({ platform, projectRoot: ctx.root, distDir, config });
|
|
958
967
|
const sizeOk = sizeCheckPlatform(platform, distDir);
|
|
959
968
|
const isoOk = verifyIsolationPlatform(platform, distDir, ctx.root);
|
|
960
969
|
const allOk = injectOk && sizeOk && isoOk;
|
|
961
970
|
if (allOk) {
|
|
962
|
-
console.log(
|
|
971
|
+
console.log(pc6.green(pc6.bold(`\u2713 [${platform}] \u6784\u5EFA + \u6821\u9A8C\u5B8C\u6210 \u2192 ${relative(ctx.root, distDir)}
|
|
963
972
|
`)));
|
|
964
973
|
} else {
|
|
965
|
-
console.log(
|
|
974
|
+
console.log(pc6.red(pc6.bold(`\u2717 [${platform}] \u6821\u9A8C/\u6CE8\u5165\u672A\u5168\u90E8\u901A\u8FC7
|
|
966
975
|
`)));
|
|
967
976
|
}
|
|
968
977
|
return allOk;
|
|
969
978
|
} catch (e) {
|
|
970
|
-
console.log(
|
|
971
|
-
if (e.stack) console.log(
|
|
979
|
+
console.log(pc6.red(` \u2717 [${platform}] vite build \u5931\u8D25:${e.message}`));
|
|
980
|
+
if (e.stack) console.log(pc6.gray(e.stack ?? ""));
|
|
972
981
|
return false;
|
|
973
982
|
} finally {
|
|
974
983
|
for (const f of tempFiles) {
|
|
@@ -981,37 +990,37 @@ async function buildPlatform(platform, ctx, config, vite) {
|
|
|
981
990
|
}
|
|
982
991
|
async function runBuild(target) {
|
|
983
992
|
if (!target) {
|
|
984
|
-
console.log(
|
|
993
|
+
console.log(pc6.red("\u7528\u6CD5:pf build <web|tiktok|wechat|facebook|capacitor|all>"));
|
|
985
994
|
return false;
|
|
986
995
|
}
|
|
987
996
|
const platforms = target === "all" ? ALL_PLATFORMS : isPlatform(target) ? [target] : null;
|
|
988
997
|
if (!platforms) {
|
|
989
|
-
console.log(
|
|
990
|
-
console.log(
|
|
998
|
+
console.log(pc6.red(`\u672A\u77E5\u5E73\u53F0:${target}`));
|
|
999
|
+
console.log(pc6.gray(`\u53EF\u9009:${ALL_PLATFORMS.join(" / ")} / all`));
|
|
991
1000
|
return false;
|
|
992
1001
|
}
|
|
993
1002
|
const ctx = resolveProject();
|
|
994
1003
|
const config = await loadPlatformConfig(ctx);
|
|
995
1004
|
const vite = await loadViteFromRoot(ctx.root);
|
|
996
|
-
console.log(
|
|
1005
|
+
console.log(pc6.cyan(pc6.bold(`\u25B6 \u5F00\u59CB\u6784\u5EFA:${platforms.join(", ")}(\u9879\u76EE:${ctx.root})
|
|
997
1006
|
`)));
|
|
998
1007
|
const results = [];
|
|
999
1008
|
for (const p of platforms) {
|
|
1000
1009
|
results.push({ platform: p, ok: await buildPlatform(p, ctx, config, vite) });
|
|
1001
1010
|
}
|
|
1002
|
-
console.log(
|
|
1011
|
+
console.log(pc6.cyan(pc6.bold("\u25B6 \u6784\u5EFA\u6C47\u603B")));
|
|
1003
1012
|
let allOk = true;
|
|
1004
1013
|
for (const r of results) {
|
|
1005
|
-
if (r.ok) console.log(
|
|
1014
|
+
if (r.ok) console.log(pc6.green(` \u2713 ${r.platform}`));
|
|
1006
1015
|
else {
|
|
1007
|
-
console.log(
|
|
1016
|
+
console.log(pc6.red(` \u2717 ${r.platform}`));
|
|
1008
1017
|
allOk = false;
|
|
1009
1018
|
}
|
|
1010
1019
|
}
|
|
1011
1020
|
if (!allOk) {
|
|
1012
|
-
console.log(
|
|
1021
|
+
console.log(pc6.red(pc6.bold("\n\u2717 \u5B58\u5728\u6784\u5EFA/\u6821\u9A8C\u5931\u8D25\u7684\u5E73\u53F0")));
|
|
1013
1022
|
} else {
|
|
1014
|
-
console.log(
|
|
1023
|
+
console.log(pc6.green(pc6.bold("\n\u2713 \u5168\u90E8\u5B8C\u6210")));
|
|
1015
1024
|
}
|
|
1016
1025
|
return allOk;
|
|
1017
1026
|
}
|
|
@@ -1035,35 +1044,33 @@ function renderHtmlShell2(platform, config, scriptSrc) {
|
|
|
1035
1044
|
async function runDev(target) {
|
|
1036
1045
|
const platform = target ? isPlatform(target) ? target : invalidTarget3(target) : "web";
|
|
1037
1046
|
if (platform === "wechat") {
|
|
1038
|
-
console.log(
|
|
1039
|
-
console.log(
|
|
1047
|
+
console.log(pc6.red("\u2717 wechat \u65E0 HTML dev server \u5F62\u6001\u3002"));
|
|
1048
|
+
console.log(pc6.yellow(" \u8BF7\u7528 `pf build wechat` \u4EA7\u51FA dist/wechat,\u518D\u7528\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u6253\u5F00\u9884\u89C8\u3002"));
|
|
1040
1049
|
return false;
|
|
1041
1050
|
}
|
|
1042
1051
|
const ctx = resolveProject();
|
|
1043
1052
|
const config = await loadPlatformConfig(ctx);
|
|
1044
1053
|
const vite = await loadViteFromRoot(ctx.root);
|
|
1045
1054
|
const cache = cacheDir2(ctx.root);
|
|
1046
|
-
fse4.ensureDirSync(cache);
|
|
1047
1055
|
const hash = randomBytes(4).toString("hex");
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
const
|
|
1051
|
-
const
|
|
1056
|
+
const runDir = join(cache, `dev-${platform}-${hash}`);
|
|
1057
|
+
fse4.ensureDirSync(runDir);
|
|
1058
|
+
const entryFileName = `main.${platform}.ts`;
|
|
1059
|
+
const entryPath = join(runDir, entryFileName);
|
|
1060
|
+
const htmlPath = join(runDir, "index.html");
|
|
1052
1061
|
fse4.writeFileSync(entryPath, renderEntry({ platform, projectRoot: ctx.root }), "utf-8");
|
|
1053
1062
|
fse4.writeFileSync(htmlPath, renderHtmlShell2(platform, config, `./${entryFileName}`), "utf-8");
|
|
1054
1063
|
const cleanup = () => {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
} catch {
|
|
1059
|
-
}
|
|
1064
|
+
try {
|
|
1065
|
+
fse4.removeSync(runDir);
|
|
1066
|
+
} catch {
|
|
1060
1067
|
}
|
|
1061
1068
|
};
|
|
1062
1069
|
try {
|
|
1063
1070
|
const server = await vite.createServer({
|
|
1064
|
-
// root
|
|
1065
|
-
// alias/publicDir
|
|
1066
|
-
root:
|
|
1071
|
+
// root 设为本次 dev 的独立子目录(其根有 index.html),浏览器打开 / 即入口页;
|
|
1072
|
+
// alias/publicDir 用绝对路径不受 root 影响
|
|
1073
|
+
root: runDir,
|
|
1067
1074
|
base: "/",
|
|
1068
1075
|
configFile: false,
|
|
1069
1076
|
define: {
|
|
@@ -1075,7 +1082,14 @@ async function runDev(target) {
|
|
|
1075
1082
|
alias: [
|
|
1076
1083
|
{ find: /^@game$/, replacement: resolve(ctx.root, "src/game") },
|
|
1077
1084
|
{ find: /^@game\//, replacement: resolve(ctx.root, "src/game") + "/" }
|
|
1078
|
-
]
|
|
1085
|
+
],
|
|
1086
|
+
// 去重 phaser,避免框架包与业务解析到不同实例
|
|
1087
|
+
dedupe: ["phaser"]
|
|
1088
|
+
},
|
|
1089
|
+
// 必须预打包 phaser(及框架包)为带 default 导出的 ESM,
|
|
1090
|
+
// 否则 dev 下 `import Phaser from 'phaser'` 报 "does not provide an export named 'default'"。
|
|
1091
|
+
optimizeDeps: {
|
|
1092
|
+
include: ["phaser", "@maoyugames/phaser-framework"]
|
|
1079
1093
|
},
|
|
1080
1094
|
publicDir: resolve(ctx.root, "src/game/public"),
|
|
1081
1095
|
server: { open: false }
|
|
@@ -1083,9 +1097,9 @@ async function runDev(target) {
|
|
|
1083
1097
|
await server.listen();
|
|
1084
1098
|
const info = server.resolvedUrls;
|
|
1085
1099
|
const url = info?.local?.[0] ?? "http://localhost:5173/";
|
|
1086
|
-
console.log(
|
|
1087
|
-
console.log(` \u6253\u5F00:${
|
|
1088
|
-
console.log(
|
|
1100
|
+
console.log(pc6.green(pc6.bold(`\u25B6 [${platform}] dev server \u5C31\u7EEA`)));
|
|
1101
|
+
console.log(` \u6253\u5F00:${pc6.cyan(url)}`);
|
|
1102
|
+
console.log(pc6.gray(" (Ctrl+C \u9000\u51FA,\u4E34\u65F6\u5165\u53E3\u4F1A\u81EA\u52A8\u6E05\u7406)"));
|
|
1089
1103
|
const onExit = async () => {
|
|
1090
1104
|
cleanup();
|
|
1091
1105
|
await server.close();
|
|
@@ -1096,17 +1110,208 @@ async function runDev(target) {
|
|
|
1096
1110
|
return true;
|
|
1097
1111
|
} catch (e) {
|
|
1098
1112
|
cleanup();
|
|
1099
|
-
console.log(
|
|
1113
|
+
console.log(pc6.red(`\u2717 dev server \u542F\u52A8\u5931\u8D25:${e.message}`));
|
|
1100
1114
|
return false;
|
|
1101
1115
|
}
|
|
1102
1116
|
}
|
|
1103
1117
|
function invalidTarget3(target) {
|
|
1104
|
-
console.log(
|
|
1118
|
+
console.log(pc6.red(`\u672A\u77E5\u5E73\u53F0:${target}`));
|
|
1119
|
+
process.exit(1);
|
|
1120
|
+
}
|
|
1121
|
+
var CONTENT_TYPES = {
|
|
1122
|
+
".html": "text/html; charset=utf-8",
|
|
1123
|
+
".js": "text/javascript; charset=utf-8",
|
|
1124
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
1125
|
+
".json": "application/json; charset=utf-8",
|
|
1126
|
+
".css": "text/css; charset=utf-8",
|
|
1127
|
+
".png": "image/png",
|
|
1128
|
+
".jpg": "image/jpeg",
|
|
1129
|
+
".jpeg": "image/jpeg",
|
|
1130
|
+
".webp": "image/webp",
|
|
1131
|
+
".svg": "image/svg+xml",
|
|
1132
|
+
".wasm": "application/wasm"
|
|
1133
|
+
};
|
|
1134
|
+
function serveDist(distDir) {
|
|
1135
|
+
const server = http.createServer((req, res) => {
|
|
1136
|
+
let p = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
1137
|
+
if (p === "/") p = "/index.html";
|
|
1138
|
+
if (p === "/favicon.ico") {
|
|
1139
|
+
res.statusCode = 204;
|
|
1140
|
+
res.end();
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
const fp = join(distDir, p);
|
|
1144
|
+
if (!resolve(fp).startsWith(resolve(distDir))) {
|
|
1145
|
+
res.statusCode = 403;
|
|
1146
|
+
res.end("forbidden");
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
readFile(fp, (err, data) => {
|
|
1150
|
+
if (err) {
|
|
1151
|
+
res.statusCode = 404;
|
|
1152
|
+
res.end("not found");
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
res.setHeader("Content-Type", CONTENT_TYPES[extname(fp).toLowerCase()] ?? "application/octet-stream");
|
|
1156
|
+
res.end(data);
|
|
1157
|
+
});
|
|
1158
|
+
});
|
|
1159
|
+
return new Promise((r) => {
|
|
1160
|
+
server.listen(0, "127.0.0.1", () => {
|
|
1161
|
+
const addr = server.address();
|
|
1162
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
1163
|
+
r({ server, port });
|
|
1164
|
+
});
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
function resolvePlaywright(root) {
|
|
1168
|
+
const req = createRequire(join(root, "package.json"));
|
|
1169
|
+
for (const name of ["playwright", "playwright-core", "@playwright/test"]) {
|
|
1170
|
+
try {
|
|
1171
|
+
const mod = req(name);
|
|
1172
|
+
if (mod && mod.chromium) return { chromium: mod.chromium };
|
|
1173
|
+
} catch {
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
return null;
|
|
1177
|
+
}
|
|
1178
|
+
async function readViewport(ctx) {
|
|
1179
|
+
try {
|
|
1180
|
+
const mod = await loadConfigModule(ctx.root, ctx.gameConfigPath);
|
|
1181
|
+
const cfg = mod.gameConfig ?? mod.default ?? {};
|
|
1182
|
+
const width = Number(cfg.designWidth) || 720;
|
|
1183
|
+
const height = Number(cfg.designHeight) || 1280;
|
|
1184
|
+
return { width, height };
|
|
1185
|
+
} catch {
|
|
1186
|
+
return { width: 720, height: 1280 };
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
async function runVerify(target, opts = {}) {
|
|
1190
|
+
const platform = target ? isPlatform(target) ? target : invalid(target) : "web";
|
|
1191
|
+
if (platform === "wechat") {
|
|
1192
|
+
console.log(pc6.yellow("\u26A0 wechat \u65E0 DOM\u3001\u9700\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177,\u65E0\u6CD5\u7528\u6D4F\u89C8\u5668 verify\u3002"));
|
|
1193
|
+
console.log(pc6.gray(" \u8BF7 `pf build wechat` \u540E\u7528\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u6253\u5F00 dist/wechat \u9884\u89C8\u9A8C\u8BC1\u3002"));
|
|
1194
|
+
return false;
|
|
1195
|
+
}
|
|
1196
|
+
const ctx = resolveProject();
|
|
1197
|
+
const pw = resolvePlaywright(ctx.root);
|
|
1198
|
+
if (!pw) {
|
|
1199
|
+
console.log(pc6.red("\u2717 pf verify \u9700\u8981 Playwright(\u53EF\u9009\u4F9D\u8D56,\u672A\u5B89\u88C5)\u3002"));
|
|
1200
|
+
console.log(pc6.yellow(" \u5B89\u88C5:") + pc6.cyan(" npm i -D playwright"));
|
|
1201
|
+
console.log(pc6.gray(" \u6D4F\u89C8\u5668\u4F18\u5148\u7528\u7CFB\u7EDF Chrome(channel:chrome),\u901A\u5E38\u65E0\u9700 `npx playwright install`\u3002"));
|
|
1202
|
+
return false;
|
|
1203
|
+
}
|
|
1204
|
+
if (!opts.noBuild) {
|
|
1205
|
+
console.log(pc6.cyan(pc6.bold(`\u25B6 [verify] \u5148\u6784\u5EFA ${platform}`)));
|
|
1206
|
+
const built = await runBuild(platform);
|
|
1207
|
+
if (!built) {
|
|
1208
|
+
console.log(pc6.red("\u2717 [verify] \u6784\u5EFA\u5931\u8D25,\u7EC8\u6B62\u9A8C\u8BC1"));
|
|
1209
|
+
return false;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
const distDir = resolve(ctx.root, "dist", platform);
|
|
1213
|
+
if (!existsSync(join(distDir, "index.html"))) {
|
|
1214
|
+
console.log(pc6.red(`\u2717 [verify] \u627E\u4E0D\u5230 ${join(distDir, "index.html")}(\u5148 build \u6216\u53BB\u6389 --no-build)`));
|
|
1215
|
+
return false;
|
|
1216
|
+
}
|
|
1217
|
+
const viewport = await readViewport(ctx);
|
|
1218
|
+
const { server, port } = await serveDist(distDir);
|
|
1219
|
+
const url = `http://127.0.0.1:${port}/`;
|
|
1220
|
+
console.log(pc6.cyan(pc6.bold(`\u25B6 [verify] \u65E0\u5934\u6D4F\u89C8\u5668\u6253\u5F00 ${platform} \u4EA7\u7269`)));
|
|
1221
|
+
const launchArgs = ["--no-sandbox", "--disable-webgl", "--disable-webgl2"];
|
|
1222
|
+
let browser;
|
|
1223
|
+
try {
|
|
1224
|
+
browser = await pw.chromium.launch({ channel: "chrome", headless: true, args: launchArgs });
|
|
1225
|
+
} catch {
|
|
1226
|
+
try {
|
|
1227
|
+
browser = await pw.chromium.launch({ headless: true, args: launchArgs });
|
|
1228
|
+
} catch (e) {
|
|
1229
|
+
server.close();
|
|
1230
|
+
console.log(pc6.red(`\u2717 [verify] \u6D4F\u89C8\u5668\u542F\u52A8\u5931\u8D25:${e.message}`));
|
|
1231
|
+
console.log(pc6.yellow(" \u88C5\u7CFB\u7EDF Chrome,\u6216\u8FD0\u884C `npx playwright install chromium`\u3002"));
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
const errors = [];
|
|
1236
|
+
let rendered = false;
|
|
1237
|
+
let distinctColors = 0;
|
|
1238
|
+
let canvasInfo = null;
|
|
1239
|
+
try {
|
|
1240
|
+
const page = await browser.newPage({ viewport });
|
|
1241
|
+
page.on("pageerror", (e) => errors.push("PAGEERROR: " + e.message));
|
|
1242
|
+
page.on("console", (m) => {
|
|
1243
|
+
if (m.type() === "error" && !/favicon/i.test(m.text())) errors.push("CONSOLE: " + m.text());
|
|
1244
|
+
});
|
|
1245
|
+
await page.goto(url, { waitUntil: "load", timeout: 3e4 });
|
|
1246
|
+
for (let i = 0; i < 20; i++) {
|
|
1247
|
+
await page.waitForTimeout(1e3);
|
|
1248
|
+
const stat = await page.evaluate(() => {
|
|
1249
|
+
const c = document.querySelector("canvas");
|
|
1250
|
+
if (!c) return { ok: false, distinct: 0, w: 0, h: 0 };
|
|
1251
|
+
const cx = c.getContext("2d");
|
|
1252
|
+
if (!cx) return { ok: false, distinct: 0, w: c.width, h: c.height };
|
|
1253
|
+
const w = c.width;
|
|
1254
|
+
const h = c.height;
|
|
1255
|
+
const data = cx.getImageData(0, 0, w, h).data;
|
|
1256
|
+
const colors = /* @__PURE__ */ new Set();
|
|
1257
|
+
const total = w * h;
|
|
1258
|
+
const step = Math.max(1, Math.floor(total / 3e3));
|
|
1259
|
+
for (let k = 0; k < total && colors.size < 40; k += step) {
|
|
1260
|
+
const o = k * 4;
|
|
1261
|
+
colors.add(`${data[o]},${data[o + 1]},${data[o + 2]}`);
|
|
1262
|
+
}
|
|
1263
|
+
return { ok: true, distinct: colors.size, w, h };
|
|
1264
|
+
}).catch(() => ({ ok: false, distinct: 0, w: 0, h: 0 }));
|
|
1265
|
+
canvasInfo = stat.w ? { w: stat.w, h: stat.h } : canvasInfo;
|
|
1266
|
+
distinctColors = stat.distinct;
|
|
1267
|
+
if (stat.ok && stat.distinct > 3) {
|
|
1268
|
+
rendered = true;
|
|
1269
|
+
break;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
const durl = await page.evaluate(() => {
|
|
1273
|
+
const c = document.querySelector("canvas");
|
|
1274
|
+
return c ? c.toDataURL("image/png") : "";
|
|
1275
|
+
}).catch(() => "");
|
|
1276
|
+
if (durl) {
|
|
1277
|
+
const outDir = join(ctx.root, ".pf-verify");
|
|
1278
|
+
mkdirSync(outDir, { recursive: true });
|
|
1279
|
+
const shot = join(outDir, `${platform}.png`);
|
|
1280
|
+
writeFileSync(shot, Buffer.from(durl.split(",")[1], "base64"));
|
|
1281
|
+
console.log(pc6.gray(` \u622A\u56FE:${shot}`));
|
|
1282
|
+
}
|
|
1283
|
+
} catch (e) {
|
|
1284
|
+
errors.push("VERIFY: " + e.message);
|
|
1285
|
+
} finally {
|
|
1286
|
+
await browser.close();
|
|
1287
|
+
server.close();
|
|
1288
|
+
}
|
|
1289
|
+
const ok = errors.length === 0 && rendered;
|
|
1290
|
+
console.log("");
|
|
1291
|
+
console.log(pc6.bold("\u25B6 [verify] \u7ED3\u679C"));
|
|
1292
|
+
console.log(` canvas:${canvasInfo ? `${canvasInfo.w}\xD7${canvasInfo.h}` : "\u65E0"};\u753B\u9762\u989C\u8272\u6570:${distinctColors}(>3 \u89C6\u4E3A\u5DF2\u6E32\u67D3)`);
|
|
1293
|
+
if (errors.length) {
|
|
1294
|
+
console.log(pc6.red(` \u63A7\u5236\u53F0/\u9875\u9762\u9519\u8BEF(${errors.length}):`));
|
|
1295
|
+
errors.slice(0, 12).forEach((e) => console.log(pc6.red(" " + e)));
|
|
1296
|
+
} else {
|
|
1297
|
+
console.log(pc6.green(" \u65E0\u63A7\u5236\u53F0/\u9875\u9762\u9519\u8BEF"));
|
|
1298
|
+
}
|
|
1299
|
+
if (ok) {
|
|
1300
|
+
console.log(pc6.green(pc6.bold(`
|
|
1301
|
+
\u2713 [verify] ${platform} \u5B9E\u8DD1\u901A\u8FC7(\u6E32\u67D3\u6B63\u5E38\u3001\u65E0\u8FD0\u884C\u671F\u9519\u8BEF)`)));
|
|
1302
|
+
} else {
|
|
1303
|
+
console.log(pc6.red(pc6.bold(`
|
|
1304
|
+
\u2717 [verify] ${platform} \u672A\u901A\u8FC7:${!rendered ? "\u753B\u9762\u672A\u6E32\u67D3\u51FA\u5185\u5BB9(\u7591\u4F3C\u5D29\u6E83/\u9ED1\u5C4F)" : "\u5B58\u5728\u8FD0\u884C\u671F\u9519\u8BEF"}`)));
|
|
1305
|
+
}
|
|
1306
|
+
return ok;
|
|
1307
|
+
}
|
|
1308
|
+
function invalid(target) {
|
|
1309
|
+
console.log(pc6.red(`\u672A\u77E5\u5E73\u53F0:${target}`));
|
|
1105
1310
|
process.exit(1);
|
|
1106
1311
|
}
|
|
1107
1312
|
function spawnCap(projectRoot, args) {
|
|
1108
1313
|
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
1109
|
-
console.log(
|
|
1314
|
+
console.log(pc6.gray(` $ ${npx} cap ${args.join(" ")}`));
|
|
1110
1315
|
const res = spawnSync(npx, ["cap", ...args], {
|
|
1111
1316
|
cwd: projectRoot,
|
|
1112
1317
|
stdio: "inherit",
|
|
@@ -1114,9 +1319,9 @@ function spawnCap(projectRoot, args) {
|
|
|
1114
1319
|
shell: process.platform === "win32"
|
|
1115
1320
|
});
|
|
1116
1321
|
if (res.error) {
|
|
1117
|
-
console.log(
|
|
1322
|
+
console.log(pc6.red(` \u2717 \u6267\u884C\u5931\u8D25:${res.error.message}`));
|
|
1118
1323
|
console.log(
|
|
1119
|
-
|
|
1324
|
+
pc6.yellow(
|
|
1120
1325
|
" \u8BF7\u786E\u8BA4\u5DF2\u5B89\u88C5 Capacitor CLI:npm i -D @capacitor/cli @capacitor/android @capacitor/ios"
|
|
1121
1326
|
)
|
|
1122
1327
|
);
|
|
@@ -1127,11 +1332,11 @@ function spawnCap(projectRoot, args) {
|
|
|
1127
1332
|
function runCap(action, platform) {
|
|
1128
1333
|
const ctx = resolveProject();
|
|
1129
1334
|
if (action === "sync") {
|
|
1130
|
-
console.log(
|
|
1335
|
+
console.log(pc6.cyan(pc6.bold("\u25B6 Capacitor sync")));
|
|
1131
1336
|
const distDir = resolve(ctx.root, "dist", "capacitor");
|
|
1132
1337
|
if (!fse4.existsSync(distDir)) {
|
|
1133
|
-
console.log(
|
|
1134
|
-
console.log(
|
|
1338
|
+
console.log(pc6.red("\u2717 dist/capacitor \u4E0D\u5B58\u5728"));
|
|
1339
|
+
console.log(pc6.yellow(" \u8BF7\u5148\u6784\u5EFA:pf build capacitor"));
|
|
1135
1340
|
return false;
|
|
1136
1341
|
}
|
|
1137
1342
|
const args = platform ? ["sync", platform] : ["sync"];
|
|
@@ -1139,13 +1344,13 @@ function runCap(action, platform) {
|
|
|
1139
1344
|
}
|
|
1140
1345
|
if (action === "open") {
|
|
1141
1346
|
if (platform !== "android" && platform !== "ios") {
|
|
1142
|
-
console.log(
|
|
1347
|
+
console.log(pc6.red("\u2717 open \u9700\u6307\u5B9A\u5E73\u53F0:pf cap open <android|ios>"));
|
|
1143
1348
|
return false;
|
|
1144
1349
|
}
|
|
1145
|
-
console.log(
|
|
1350
|
+
console.log(pc6.cyan(pc6.bold(`\u25B6 Capacitor open ${platform}`)));
|
|
1146
1351
|
return capExec(ctx.root, ["open", platform]);
|
|
1147
1352
|
}
|
|
1148
|
-
console.log(
|
|
1353
|
+
console.log(pc6.red("\u7528\u6CD5:"));
|
|
1149
1354
|
console.log(" pf cap sync [android|ios]");
|
|
1150
1355
|
console.log(" pf cap open <android|ios>");
|
|
1151
1356
|
return false;
|
|
@@ -1184,35 +1389,43 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1184
1389
|
process.exitCode = ok ? 0 : 1;
|
|
1185
1390
|
return;
|
|
1186
1391
|
}
|
|
1392
|
+
case "verify": {
|
|
1393
|
+
const noBuild = rest.includes("--no-build");
|
|
1394
|
+
const platform = rest.find((a) => !a.startsWith("-"));
|
|
1395
|
+
const ok = await runVerify(platform, { noBuild });
|
|
1396
|
+
process.exitCode = ok ? 0 : 1;
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1187
1399
|
case "cap": {
|
|
1188
1400
|
const ok = runCap(rest[0], rest[1]);
|
|
1189
1401
|
process.exitCode = ok ? 0 : 1;
|
|
1190
1402
|
return;
|
|
1191
1403
|
}
|
|
1192
1404
|
default: {
|
|
1193
|
-
console.log(
|
|
1194
|
-
console.log(
|
|
1405
|
+
console.log(pc6.yellow(`[pf] \u672A\u77E5\u547D\u4EE4 "${command}"\u3002`));
|
|
1406
|
+
console.log(pc6.dim("\u8FD0\u884C `pf help` \u67E5\u770B\u53EF\u7528\u547D\u4EE4\u3002"));
|
|
1195
1407
|
process.exitCode = 1;
|
|
1196
1408
|
}
|
|
1197
1409
|
}
|
|
1198
1410
|
} catch (e) {
|
|
1199
|
-
console.log(
|
|
1200
|
-
if (e.stack) console.log(
|
|
1411
|
+
console.log(pc6.red(`[pf] \u6267\u884C "${command}" \u51FA\u9519:${e.message}`));
|
|
1412
|
+
if (e.stack) console.log(pc6.gray(e.stack ?? ""));
|
|
1201
1413
|
process.exitCode = 1;
|
|
1202
1414
|
}
|
|
1203
1415
|
}
|
|
1204
1416
|
function printHelp() {
|
|
1205
|
-
console.log(
|
|
1206
|
-
console.log(
|
|
1417
|
+
console.log(pc6.bold("@maoyugames/phaser-framework CLI (pf)"));
|
|
1418
|
+
console.log(pc6.dim("\u591A\u5E73\u53F0 Phaser \u6846\u67B6\u6784\u5EFA\u5DE5\u5177\u3002"));
|
|
1207
1419
|
console.log("");
|
|
1208
1420
|
console.log("\u547D\u4EE4:");
|
|
1209
|
-
console.log(` ${
|
|
1210
|
-
console.log(` ${
|
|
1211
|
-
console.log(` ${
|
|
1212
|
-
console.log(` ${
|
|
1213
|
-
console.log(` ${
|
|
1421
|
+
console.log(` ${pc6.cyan("pf dev [platform]")} \u542F\u52A8\u67D0\u5E73\u53F0 dev server(\u9ED8\u8BA4 web)`);
|
|
1422
|
+
console.log(` ${pc6.cyan("pf build <platform|all>")} \u6784\u5EFA\u67D0\u5E73\u53F0/\u5168\u90E8\u5E73\u53F0`);
|
|
1423
|
+
console.log(` ${pc6.cyan("pf size-check [platform]")} \u4EA7\u7269\u4F53\u79EF\u68C0\u67E5`);
|
|
1424
|
+
console.log(` ${pc6.cyan("pf verify-isolation [platform]")} SDK \u9694\u79BB\u6821\u9A8C`);
|
|
1425
|
+
console.log(` ${pc6.cyan("pf verify [platform] [--no-build]")} \u5B9E\u8DD1\u5192\u70DF\u9A8C\u8BC1(Playwright \u65E0\u5934:\u6E32\u67D3+\u65E0\u8FD0\u884C\u671F\u9519\u8BEF)`);
|
|
1426
|
+
console.log(` ${pc6.cyan("pf cap <sync|open> [android|ios]")} Capacitor \u539F\u751F\u5DE5\u7A0B\u64CD\u4F5C`);
|
|
1214
1427
|
console.log("");
|
|
1215
|
-
console.log(
|
|
1428
|
+
console.log(pc6.dim("\u5E73\u53F0:web / tiktok / wechat / facebook / capacitor"));
|
|
1216
1429
|
}
|
|
1217
1430
|
void main();
|
|
1218
1431
|
|
package/dist/index.js
CHANGED
|
@@ -2091,6 +2091,15 @@ var AccountService = class {
|
|
|
2091
2091
|
}
|
|
2092
2092
|
};
|
|
2093
2093
|
|
|
2094
|
+
// src/scene/active-scene.ts
|
|
2095
|
+
var _active = null;
|
|
2096
|
+
function setActiveScene(scene) {
|
|
2097
|
+
_active = scene;
|
|
2098
|
+
}
|
|
2099
|
+
function getActiveScene() {
|
|
2100
|
+
return _active;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2094
2103
|
// src/services/SceneManager.ts
|
|
2095
2104
|
var SceneManager = class {
|
|
2096
2105
|
constructor() {
|
|
@@ -2108,19 +2117,18 @@ var SceneManager = class {
|
|
|
2108
2117
|
return;
|
|
2109
2118
|
}
|
|
2110
2119
|
const sceneData = asSceneData(data);
|
|
2111
|
-
const
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2120
|
+
const active = getActiveScene();
|
|
2121
|
+
if (active && active.scene && typeof active.scene.start === "function") {
|
|
2122
|
+
for (const scene of manager.getScenes(true)) {
|
|
2123
|
+
const sceneKey = scene.scene.key;
|
|
2124
|
+
if (sceneKey !== key && scene !== active) {
|
|
2125
|
+
manager.stop(sceneKey);
|
|
2126
|
+
}
|
|
2117
2127
|
}
|
|
2128
|
+
active.scene.start(key, sceneData);
|
|
2129
|
+
return;
|
|
2118
2130
|
}
|
|
2119
|
-
|
|
2120
|
-
vehicle.scene.start(key, sceneData);
|
|
2121
|
-
} else {
|
|
2122
|
-
manager.start(key, sceneData);
|
|
2123
|
-
}
|
|
2131
|
+
manager.start(key, sceneData);
|
|
2124
2132
|
}
|
|
2125
2133
|
push(key, data) {
|
|
2126
2134
|
const manager = this.game?.scene;
|
|
@@ -2868,9 +2876,6 @@ var PanelManager = class {
|
|
|
2868
2876
|
return actives[actives.length - 1];
|
|
2869
2877
|
}
|
|
2870
2878
|
};
|
|
2871
|
-
|
|
2872
|
-
// src/bootstrap.ts
|
|
2873
|
-
var _game = null;
|
|
2874
2879
|
async function startGame(opts) {
|
|
2875
2880
|
const { platform, config, scenes } = opts;
|
|
2876
2881
|
PlatformContext.set(platform);
|
|
@@ -2932,31 +2937,34 @@ async function startGame(opts) {
|
|
|
2932
2937
|
await opts.setup(App);
|
|
2933
2938
|
}
|
|
2934
2939
|
log.info(`[bootstrap] \u5E73\u53F0 ${platform.info.name} \u521D\u59CB\u5316\u5B8C\u6210,\u542F\u52A8 Phaser`);
|
|
2935
|
-
|
|
2936
|
-
audio.bindGame(
|
|
2937
|
-
sceneManager.bindGame(
|
|
2938
|
-
panelManager.bindGame(
|
|
2940
|
+
const game = createPhaserGameInstance(config, scenes);
|
|
2941
|
+
audio.bindGame(game);
|
|
2942
|
+
sceneManager.bindGame(game);
|
|
2943
|
+
panelManager.bindGame(game);
|
|
2944
|
+
await waitForGameReady(game);
|
|
2939
2945
|
await App.modules.initAll();
|
|
2940
2946
|
log.info("[bootstrap] \u6E38\u620F\u542F\u52A8\u5B8C\u6210");
|
|
2941
2947
|
}
|
|
2942
|
-
function
|
|
2948
|
+
function createPhaserGameInstance(config, scenes) {
|
|
2949
|
+
const gameConfig = {
|
|
2950
|
+
type: Phaser4.AUTO,
|
|
2951
|
+
// 透明背景占位;业务场景自行绘制背景
|
|
2952
|
+
backgroundColor: "#1f2937",
|
|
2953
|
+
scale: {
|
|
2954
|
+
// FIT:按设计分辨率等比缩放铺满容器(保持比例,留黑边);CENTER_BOTH:居中
|
|
2955
|
+
mode: Phaser4.Scale.FIT,
|
|
2956
|
+
autoCenter: Phaser4.Scale.CENTER_BOTH,
|
|
2957
|
+
width: config.designWidth,
|
|
2958
|
+
height: config.designHeight
|
|
2959
|
+
},
|
|
2960
|
+
scene: scenes
|
|
2961
|
+
// 微信等无 DOM 平台由 weapp-adapter 提供 canvas;有 DOM 平台 Phaser 自动建 canvas
|
|
2962
|
+
};
|
|
2963
|
+
return new Phaser4.Game(gameConfig);
|
|
2964
|
+
}
|
|
2965
|
+
function waitForGameReady(game) {
|
|
2943
2966
|
return new Promise((resolve) => {
|
|
2944
|
-
|
|
2945
|
-
type: Phaser4.AUTO,
|
|
2946
|
-
// 透明背景占位;业务场景自行绘制背景
|
|
2947
|
-
backgroundColor: "#1f2937",
|
|
2948
|
-
scale: {
|
|
2949
|
-
// FIT:按设计分辨率等比缩放铺满容器(保持比例,留黑边);CENTER_BOTH:居中
|
|
2950
|
-
mode: Phaser4.Scale.FIT,
|
|
2951
|
-
autoCenter: Phaser4.Scale.CENTER_BOTH,
|
|
2952
|
-
width: config.designWidth,
|
|
2953
|
-
height: config.designHeight
|
|
2954
|
-
},
|
|
2955
|
-
scene: scenes
|
|
2956
|
-
// 微信等无 DOM 平台由 weapp-adapter 提供 canvas;有 DOM 平台 Phaser 自动建 canvas
|
|
2957
|
-
};
|
|
2958
|
-
const game = new Phaser4.Game(gameConfig);
|
|
2959
|
-
game.events.once(Phaser4.Core.Events.READY, () => resolve(game));
|
|
2967
|
+
game.events.once(Phaser4.Core.Events.READY, () => resolve());
|
|
2960
2968
|
});
|
|
2961
2969
|
}
|
|
2962
2970
|
|
|
@@ -3094,6 +3102,7 @@ var BaseScene = class extends Phaser4.Scene {
|
|
|
3094
3102
|
* 注意:业务场景若覆写 init(data),请调用 super.init(data) 以保留自动清理(this.bind / this.scheduler)。
|
|
3095
3103
|
*/
|
|
3096
3104
|
init(_data) {
|
|
3105
|
+
setActiveScene(this);
|
|
3097
3106
|
this.armCleanupHooks();
|
|
3098
3107
|
}
|
|
3099
3108
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maoyugames/phaser-framework",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "多平台 Phaser 游戏框架:业务/底层分离,HTTP/WebSocket/KCP,Web/TikTok/微信/Facebook/App 隔离打包",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,11 +53,15 @@
|
|
|
53
53
|
"phaser": "^3.60.0 || ^3.80.0 || ^3.90.0",
|
|
54
54
|
"vite": "^5.0.0",
|
|
55
55
|
"typescript": "^5.0.0",
|
|
56
|
-
"terser": "^5.0.0"
|
|
56
|
+
"terser": "^5.0.0",
|
|
57
|
+
"playwright": "^1.40.0"
|
|
57
58
|
},
|
|
58
59
|
"peerDependenciesMeta": {
|
|
59
60
|
"terser": {
|
|
60
61
|
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"playwright": {
|
|
64
|
+
"optional": true
|
|
61
65
|
}
|
|
62
66
|
},
|
|
63
67
|
"dependencies": {
|