ttmg-pack 0.2.8 → 0.2.9-beta.2

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.
@@ -37,3 +37,17 @@ export declare const GAME_ENGINES: {
37
37
  export declare const TTMG_TEMP_DIR = "__TTMG_TEMP__";
38
38
  export declare const USELESS_DIRS: string[];
39
39
  export declare const USELESS_FILES: string[];
40
+ export declare const GAME_PKG_SIZE_LIMIT: {
41
+ common: {
42
+ project: number;
43
+ main: number;
44
+ sub: any;
45
+ independent: number;
46
+ };
47
+ unity: {
48
+ project: number;
49
+ main: any;
50
+ sub: any;
51
+ independent: any;
52
+ };
53
+ };
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * ==========================================
3
3
  * @Description: ttmg pack
4
- * @Version: 0.2.8
4
+ * @Version: 0.2.9-beta.2
5
5
  * @Author: zhanghongyang.mocha
6
- * @Date: 2026-01-15 11:54:37
6
+ * @Date: 2026-01-22 19:16:02
7
7
  * ==========================================
8
8
  */
9
9
  'use strict';
@@ -149,6 +149,13 @@ const PACKAGE_TYPE = {
149
149
  const TTMG_TEMP_DIR = '__TTMG_TEMP__';
150
150
  const USELESS_DIRS = ['node_modules', '__MACOSX', TTMG_TEMP_DIR];
151
151
  const USELESS_FILES = ['.DS_Store', 'Thumbs.db'];
152
+ const GAME_PKG_SIZE_LIMIT = {
153
+ unity: {
154
+ project: 60 * 1024 * 1024,
155
+ main: null,
156
+ independent: null,
157
+ },
158
+ };
152
159
 
153
160
  /**
154
161
  * 递归统计目录下所有文件的大小,支持过滤文件夹和文件
@@ -432,59 +439,6 @@ function getGameEngine(gameEntry) {
432
439
  }
433
440
  }
434
441
 
435
- function isUnityEngine(gameEntry) {
436
- return getGameEngine(gameEntry) === 'unity';
437
- }
438
-
439
- function getWasmBrCodePath({ entryDir, }) {
440
- if (!isUnityEngine(entryDir)) {
441
- return '';
442
- }
443
- else {
444
- /**
445
- * 检查项目下存在一个名为 wasmcode 的文件夹,且文件夹下存在一个文件后缀为 .webgl.wasm.code.unityweb.wasm.br 的文件。,如果满足条件,就展示 true,否则 返回 false
446
- */
447
- const wasmCodeDir = path.join(entryDir, 'wasmcode');
448
- if (!fs.existsSync(wasmCodeDir)) {
449
- return '';
450
- }
451
- const wasmBrFiles = fs.readdirSync(wasmCodeDir).filter(file => file.endsWith('.webgl.wasm.code.unityweb.wasm.br'));
452
- if (wasmBrFiles.length === 0) {
453
- return '';
454
- }
455
- else {
456
- return path.relative(entryDir, path.join(wasmCodeDir, wasmBrFiles[0]));
457
- }
458
- }
459
- }
460
-
461
- function getWasmBrCodeMd5({ entryDir }) {
462
- // originalWasmMd5
463
- try {
464
- const gameConfig = JSON.parse(fs.readFileSync(path.join(entryDir, 'game.json'), 'utf-8'));
465
- return gameConfig.originalWasmMd5 || '';
466
- }
467
- catch (_a) {
468
- return '';
469
- }
470
- }
471
-
472
- // 面向高鹏编程 😢
473
- /**
474
- * 覆盖配置,根据游戏引擎类型,设置主包和独立子包的大小限制
475
- * @param entryDir 游戏项目入口目录
476
- * @param config 原始构建配置
477
- * @returns 覆盖后的构建配置
478
- */
479
- function overrideConfig(config) {
480
- var _a, _b, _c;
481
- const { entry: entryDir } = config;
482
- const isUnity = isUnityEngine(entryDir);
483
- return Object.assign(Object.assign({}, config), { build: Object.assign(Object.assign({}, config.build), { pkgSizeLimit: !isUnity ? (_a = config.build) === null || _a === void 0 ? void 0 : _a.pkgSizeLimit : 60 * 1024 * 1024, mainPkgSizeLimit: !isUnity ? (_b = config.build) === null || _b === void 0 ? void 0 : _b.mainPkgSizeLimit : null, independentSubPkgSizeLimit: !isUnity
484
- ? (_c = config.build) === null || _c === void 0 ? void 0 : _c.independentSubPkgSizeLimit
485
- : null }) });
486
- }
487
-
488
442
  let initializePromise = null;
489
443
  /**
490
444
  * 确保一个异步函数在整个应用生命周期内只被执行一次。
@@ -605,6 +559,98 @@ async function genOpenDataContext(entryPath) {
605
559
  return jsCode;
606
560
  }
607
561
 
562
+ function isUnityEngine(gameEntry) {
563
+ return getGameEngine(gameEntry) === 'unity';
564
+ }
565
+
566
+ function getWasmBrCodePath({ entryDir, }) {
567
+ if (!isUnityEngine(entryDir)) {
568
+ return '';
569
+ }
570
+ else {
571
+ /**
572
+ * 检查项目下存在一个名为 wasmcode 的文件夹,且文件夹下存在一个文件后缀为 .webgl.wasm.code.unityweb.wasm.br 的文件。,如果满足条件,就展示 true,否则 返回 false
573
+ */
574
+ const wasmCodeDir = path.join(entryDir, 'wasmcode');
575
+ if (!fs.existsSync(wasmCodeDir)) {
576
+ return '';
577
+ }
578
+ const wasmBrFiles = fs.readdirSync(wasmCodeDir).filter(file => file.endsWith('.webgl.wasm.code.unityweb.wasm.br'));
579
+ if (wasmBrFiles.length === 0) {
580
+ return '';
581
+ }
582
+ else {
583
+ return path.relative(entryDir, path.join(wasmCodeDir, wasmBrFiles[0]));
584
+ }
585
+ }
586
+ }
587
+
588
+ function getWasmBrCodeMd5({ entryDir }) {
589
+ // originalWasmMd5
590
+ try {
591
+ const gameConfig = JSON.parse(fs.readFileSync(path.join(entryDir, 'game.json'), 'utf-8'));
592
+ return gameConfig.originalWasmMd5 || '';
593
+ }
594
+ catch (_a) {
595
+ return '';
596
+ }
597
+ }
598
+
599
+ function getUnityBuildConfig() {
600
+ return {
601
+ pkgSizeLimit: GAME_PKG_SIZE_LIMIT.unity.project,
602
+ mainPkgSizeLimit: GAME_PKG_SIZE_LIMIT.unity.main,
603
+ independentSubPkgSizeLimit: GAME_PKG_SIZE_LIMIT.unity.independent,
604
+ };
605
+ }
606
+
607
+ function getCheckConfig(config) {
608
+ if (isUnityEngine(config.entry)) {
609
+ return getUnityBuildConfig();
610
+ }
611
+ else {
612
+ return {
613
+ pkgSizeLimit: 30 * 1024 * 1024,
614
+ mainPkgSizeLimit: 4 * 1024 * 1024,
615
+ independentSubPkgSizeLimit: 4 * 1024 * 1024,
616
+ };
617
+ }
618
+ }
619
+
620
+ function copyDirectory(src, dest) {
621
+ try {
622
+ if (!fs.existsSync(dest)) {
623
+ fs.mkdirSync(dest, { recursive: true });
624
+ }
625
+ const items = fs.readdirSync(src);
626
+ const normalizedDest = path.resolve(dest) + path.sep;
627
+ items.forEach(item => {
628
+ const srcPath = path.join(src, item);
629
+ const destPath = path.join(dest, item);
630
+ // 检查当前要处理的源路径是否就是目标路径
631
+ // 这可以防止将目标目录本身复制到自身中
632
+ if (path.resolve(srcPath) === path.resolve(dest)) {
633
+ return; // 跳过目标目录本身
634
+ }
635
+ // 获取文件状态
636
+ const stat = fs.statSync(srcPath);
637
+ if (stat.isFile()) {
638
+ // 复制文件
639
+ fs.copyFileSync(srcPath, destPath);
640
+ }
641
+ else if (stat.isDirectory()) {
642
+ const childNormalizedSrc = path.resolve(srcPath) + path.sep;
643
+ if (!normalizedDest.startsWith(childNormalizedSrc)) {
644
+ copyDirectory(srcPath, destPath);
645
+ }
646
+ }
647
+ });
648
+ }
649
+ catch (error) {
650
+ logger.error(`copyDirectory error: ${error}`);
651
+ }
652
+ }
653
+
608
654
  /**
609
655
  * 将 packageConfig packages 中 分拆的包 配置迁移到 odr_packages 中
610
656
  */
@@ -624,7 +670,7 @@ async function writeOdrConfig(outputDir) {
624
670
  if (!odrPackages[relatedPkgName]) {
625
671
  odrPackages[relatedPkgName] = {};
626
672
  }
627
- odrPackages[relatedPkgName] = Object.assign(Object.assign({}, odrPackages[relatedPkgName]), { root: pkgConfig.root, code_output: pkgConfig.output, code_md5: pkgConfig.md5, main: pkgConfig.main });
673
+ odrPackages[relatedPkgName] = Object.assign(Object.assign({}, odrPackages[relatedPkgName]), { root: pkgConfig.root, code_output: pkgConfig.output, code_md5: pkgConfig.md5, main: pkgConfig.main, dependencies: pkgConfig.dependencies, type: pkgConfig.type });
628
674
  delete packages[pkgName];
629
675
  }
630
676
  if (pkgName.endsWith(ODR_ASSET_DIR_SUFFIX)) {
@@ -653,12 +699,14 @@ async function makeOdrPkgs(outputDir) {
653
699
  const pkgOutput = path.join(outputDir, pkgName);
654
700
  const codeOutput = path.join(outputDir, `${pkgName}${ODR_CODE_DIR_SUFFIX}`);
655
701
  const assetOutput = path.join(outputDir, `${pkgName}${ODR_ASSET_DIR_SUFFIX}`);
656
- ensureDirSync(codeOutput);
657
- ensureDirSync(assetOutput);
658
- fs.cpSync(pkgOutput, codeOutput, { recursive: true });
659
- fs.cpSync(pkgOutput, assetOutput, { recursive: true });
660
- deleteNoJsFilesSync(codeOutput);
661
- deleteJsFilesSync(assetOutput);
702
+ if (packages[pkgName].type === 'game') {
703
+ ensureDirSync(codeOutput);
704
+ ensureDirSync(assetOutput);
705
+ fs.cpSync(pkgOutput, codeOutput, { recursive: true });
706
+ fs.cpSync(pkgOutput, assetOutput, { recursive: true });
707
+ deleteNoJsFilesSync(codeOutput);
708
+ deleteJsFilesSync(assetOutput);
709
+ }
662
710
  /**
663
711
  * 写入配置
664
712
  */
@@ -901,7 +949,8 @@ function checkGameJson(entryDir, config) {
901
949
  */
902
950
  function checkProjectSize(entryDir, config) {
903
951
  logger.info('start check project size');
904
- const { build: { pkgSizeLimit }, dev, } = config;
952
+ const { dev } = config;
953
+ const { pkgSizeLimit } = getCheckConfig(config);
905
954
  const enableDev = dev === null || dev === void 0 ? void 0 : dev.enable;
906
955
  const gameSize = getProjectSize({ entryDir });
907
956
  const gameSizeMB = (gameSize / (1024 * 1024)).toFixed(2);
@@ -938,11 +987,12 @@ const defaultCheckConfig$2 = {
938
987
  */
939
988
  function checkMainPackageSize({ entryDir, config, }) {
940
989
  logger.info('start check main package size');
941
- const { build, dev } = config;
990
+ const { dev } = config;
991
+ const { mainPkgSizeLimit } = getCheckConfig(config);
942
992
  const mainPkgSize = getMainPkgSize({ entryDir });
943
993
  const mainPkgSizeMB = (mainPkgSize / (1024 * 1024)).toFixed(2);
944
- const limitMB = (build.mainPkgSizeLimit / (1024 * 1024)).toFixed(2);
945
- if (mainPkgSize > build.mainPkgSizeLimit) {
994
+ const limitMB = (mainPkgSizeLimit / (1024 * 1024)).toFixed(2);
995
+ if (mainPkgSize > mainPkgSizeLimit) {
946
996
  const errMsg = `Check main package size failed, main package size ${mainPkgSizeMB}MB, must not exceed ${limitMB}MB`;
947
997
  logger.error(errMsg);
948
998
  if (dev === null || dev === void 0 ? void 0 : dev.enable) {
@@ -983,10 +1033,7 @@ function checkMainPackage(entryDir, config) {
983
1033
  entryDir,
984
1034
  config,
985
1035
  }));
986
- /**
987
- * 检查主包大小是否超出限制, 仅对非unity工程生效
988
- */
989
- if (!isUnityEngine(entryDir) && ((_a = config.build) === null || _a === void 0 ? void 0 : _a.mainPkgSizeLimit)) {
1036
+ if ((_a = getCheckConfig(config)) === null || _a === void 0 ? void 0 : _a.mainPkgSizeLimit) {
990
1037
  checkResults.push(checkMainPackageSize({
991
1038
  entryDir,
992
1039
  config,
@@ -1085,7 +1132,6 @@ function checkIndependentPackages(entryDir, config) {
1085
1132
  logger.info('start check independent subpackages in game.json');
1086
1133
  const independentSubpackages = getIndependentPackagesConfig(entryDir);
1087
1134
  independentSubpackages.forEach(sub => {
1088
- var _a;
1089
1135
  /**
1090
1136
  * 校验 independent subpackage 配置是否为空
1091
1137
  */
@@ -1132,8 +1178,8 @@ function checkIndependentPackages(entryDir, config) {
1132
1178
  /**
1133
1179
  * 校验 independent subpackage 包大小,不做校验
1134
1180
  */
1135
- const independentSubPkgSizeLimit = (_a = config === null || config === void 0 ? void 0 : config.build) === null || _a === void 0 ? void 0 : _a.independentSubPkgSizeLimit;
1136
- if (!isUnityEngine(entryDir) && independentSubPkgSizeLimit) {
1181
+ const { independentSubPkgSizeLimit } = getCheckConfig(config);
1182
+ if (independentSubPkgSizeLimit) {
1137
1183
  const checkSizeResult = checkPkgSize({
1138
1184
  entryDir: subpackageEntryDir,
1139
1185
  pkgName: sub.name,
@@ -1344,6 +1390,8 @@ async function setup(entryDir, outputDir) {
1344
1390
  root: '',
1345
1391
  main: 'game.js',
1346
1392
  output: `${GAME_MAIN_PACKAGE_NAME}${STTPKG_EXT}`,
1393
+ type: 'game',
1394
+ dependencies: []
1347
1395
  };
1348
1396
  transformSubPackages(gameJson.subpackages).forEach(({ name, root, main, independent }) => {
1349
1397
  // root 是目录
@@ -1354,6 +1402,8 @@ async function setup(entryDir, outputDir) {
1354
1402
  main,
1355
1403
  output: `${name}${STTPKG_EXT}`,
1356
1404
  independent,
1405
+ type: 'game',
1406
+ dependencies: []
1357
1407
  };
1358
1408
  if (independent) {
1359
1409
  fyfPackages.push({
@@ -1501,6 +1551,27 @@ function collectMaps(entryDir, packages) {
1501
1551
  logger.info('基于游戏源代码收集分包中的 JS 文件完成');
1502
1552
  return result;
1503
1553
  }
1554
+ function collectPluginMaps(gameDir, entryDir, name) {
1555
+ logger.info('开始基于插件源代码收集分包中的 JS 文件');
1556
+ const result = {};
1557
+ let jsFiles;
1558
+ jsFiles = collectPkgJsFiles({
1559
+ entry: entryDir,
1560
+ root: entryDir,
1561
+ exts: ['.js', '.ts', '.jsx', '.tsx'],
1562
+ excludeDirs: [],
1563
+ });
1564
+ const list = [];
1565
+ jsFiles.forEach(absPath => {
1566
+ list.push(path.relative(gameDir, absPath).replace(/\\/g, '/'));
1567
+ });
1568
+ const packNameRoot = name;
1569
+ result[name] = {
1570
+ [`${packNameRoot}/game.pack.js`]: list,
1571
+ };
1572
+ logger.info('基于插件源代码收集分包中的 JS 文件完成');
1573
+ return result;
1574
+ }
1504
1575
 
1505
1576
  function collectDeps(gameEntry, packages) {
1506
1577
  // 自动扫描 game 目录下的一级文件夹作为根前缀
@@ -1553,6 +1624,7 @@ function collectDeps(gameEntry, packages) {
1553
1624
  }
1554
1625
  requireCalls.push({
1555
1626
  file: path.relative(gameEntry, fullPath),
1627
+ type: 'game',
1556
1628
  callee: node.callee.name,
1557
1629
  requirePath,
1558
1630
  resolvedPath: resolvedPath
@@ -1560,6 +1632,20 @@ function collectDeps(gameEntry, packages) {
1560
1632
  : '',
1561
1633
  });
1562
1634
  }
1635
+ else if (node.type === 'CallExpression' &&
1636
+ node.callee.type === 'Identifier' &&
1637
+ node.callee.name === 'requirePlugin' &&
1638
+ node.arguments.length > 0 &&
1639
+ node.arguments[0].type === 'Literal') {
1640
+ const requirePath = node.arguments[0].value;
1641
+ requireCalls.push({
1642
+ file: path.relative(gameEntry, fullPath),
1643
+ type: 'plugin',
1644
+ callee: node.callee.name,
1645
+ requirePath,
1646
+ resolvedPath: requirePath,
1647
+ });
1648
+ }
1563
1649
  });
1564
1650
  });
1565
1651
  const newRequireCalls = requireCalls.map(i => {
@@ -1567,11 +1653,16 @@ function collectDeps(gameEntry, packages) {
1567
1653
  * 基于 packages 来判断
1568
1654
  */
1569
1655
  let depModule = '';
1570
- Object.keys(packages).forEach(pkg => {
1571
- if (i.resolvedPath && i.resolvedPath.startsWith(packages[pkg].root)) {
1572
- depModule = pkg;
1573
- }
1574
- });
1656
+ if (i.type === 'plugin') {
1657
+ depModule = i.requirePath.split('/')[0];
1658
+ }
1659
+ else if (i.type === 'game') {
1660
+ Object.keys(packages).forEach(pkg => {
1661
+ if (i.resolvedPath && i.resolvedPath.startsWith(packages[pkg].root)) {
1662
+ depModule = pkg;
1663
+ }
1664
+ });
1665
+ }
1575
1666
  let curModule = '';
1576
1667
  Object.keys(packages).forEach(pkg => {
1577
1668
  if (i.file.startsWith(packages[pkg].root)) {
@@ -1585,17 +1676,26 @@ function collectDeps(gameEntry, packages) {
1585
1676
  const exts = ['.js', '.ts', '.jsx', '.tsx'];
1586
1677
  const isJsLike = f => exts.some(ext => f.endsWith(ext));
1587
1678
  const result = newRequireCalls.reduce((acc, cur) => {
1588
- if (!acc[cur.curModule]) {
1589
- acc[cur.curModule] = {};
1679
+ if (!acc.packageDeps[cur.curModule]) {
1680
+ acc.packageDeps[cur.curModule] = {};
1681
+ }
1682
+ if (!acc.pluginDeps[cur.curModule]) {
1683
+ acc.pluginDeps[cur.curModule] = [];
1590
1684
  }
1591
1685
  if (cur.curModule === cur.depModule) {
1592
1686
  return acc;
1593
1687
  }
1594
- if (cur.resolvedPath && isJsLike(cur.resolvedPath)) {
1595
- acc[cur.curModule] = Object.assign(Object.assign({}, acc[cur.curModule]), { [cur.resolvedPath]: cur.depModule });
1688
+ if (cur.resolvedPath && isJsLike(cur.resolvedPath) && cur.type === 'game') {
1689
+ acc.packageDeps[cur.curModule][cur.resolvedPath] = cur.depModule;
1690
+ }
1691
+ else if (cur.type === 'plugin') {
1692
+ acc.pluginDeps[cur.curModule].push(cur.depModule);
1596
1693
  }
1597
1694
  return acc;
1598
- }, {});
1695
+ }, {
1696
+ packageDeps: {},
1697
+ pluginDeps: {},
1698
+ });
1599
1699
  return result;
1600
1700
  }
1601
1701
  function getRootPrefixes(gameEntry) {
@@ -1641,7 +1741,7 @@ async function pack({ gameEntry, pkgName, allDeps, allMaps, pkgConfig, destRoot,
1641
1741
  const packMap = allMaps[pkgName];
1642
1742
  const moduleConfig = {
1643
1743
  main: pkgConfig.main,
1644
- deps: allDeps[pkgName] || {},
1744
+ deps: allDeps.packageDeps[pkgName] || {},
1645
1745
  map: allMaps[pkgName],
1646
1746
  };
1647
1747
  ensureDirSync(destRoot);
@@ -1692,7 +1792,8 @@ async function pack({ gameEntry, pkgName, allDeps, allMaps, pkgConfig, destRoot,
1692
1792
  // });
1693
1793
  // const code = fs.readFileSync(absPath, 'utf-8');
1694
1794
  const fileId = relPath.replace(/\\/g, '/');
1695
- const defineHeader = `define("game:${fileId}", ["require", "requireAsync", "module", "exports", "sandboxGlobal"], function(require, requireAsync, module, exports, sandboxGlobal){\nwith(sandboxGlobal){\n`;
1795
+ const schema = config.build.type === 'plugin' ? 'plugin' : 'game';
1796
+ const defineHeader = `define("${schema}:${fileId}", ["require", "requireAsync", "module", "exports", "sandboxGlobal"], function(require, requireAsync, module, exports, sandboxGlobal){\nwith(sandboxGlobal){\n`;
1696
1797
  const defineFooter = `\n}\n});\n`;
1697
1798
  const fileMagic = new MagicString(code, { filename: fileId });
1698
1799
  fileMagic.prepend(defineHeader);
@@ -1779,6 +1880,75 @@ async function partition({ pkgRoot, destRoot, packedFiles, gameEntry, gameOutput
1779
1880
  }
1780
1881
  }
1781
1882
 
1883
+ /**
1884
+ * 根据 TikTok DevPortal 接口获取插件包信息(url + md5)
1885
+ *
1886
+ * 对应 curl:
1887
+ * POST https://developers.tiktok.com/tiktok/v4/devportal/minigame/plugin_meta/get
1888
+ * body: { plugin_name, plugin_version }
1889
+ */
1890
+ async function getPluginInfo(params) {
1891
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1892
+ const baseUrl = (_a = params.baseUrl) !== null && _a !== void 0 ? _a : 'https://developers.tiktok.com';
1893
+ const url = `${baseUrl}/tiktok/v4/devportal/minigame/plugin_meta/get`;
1894
+ const res = await fetch(url, {
1895
+ method: 'POST',
1896
+ headers: Object.assign({ 'Content-Type': 'application/json' }, ((_b = params.headers) !== null && _b !== void 0 ? _b : {})),
1897
+ body: JSON.stringify({
1898
+ plugin_name: params.plugin_name,
1899
+ plugin_version: params.plugin_version,
1900
+ }),
1901
+ });
1902
+ const json = await res.json();
1903
+ const meta = (_g = (_e = (_c = json === null || json === void 0 ? void 0 : json.PluginMeta) !== null && _c !== void 0 ? _c : (_d = json === null || json === void 0 ? void 0 : json.data) === null || _d === void 0 ? void 0 : _d.PluginMeta) !== null && _e !== void 0 ? _e : (_f = json === null || json === void 0 ? void 0 : json.result) === null || _f === void 0 ? void 0 : _f.PluginMeta) !== null && _g !== void 0 ? _g : (_j = (_h = json === null || json === void 0 ? void 0 : json.data) === null || _h === void 0 ? void 0 : _h.result) === null || _j === void 0 ? void 0 : _j.PluginMeta;
1904
+ if (!(meta === null || meta === void 0 ? void 0 : meta.PackageCDNURL) || !(meta === null || meta === void 0 ? void 0 : meta.PackageMD5)) {
1905
+ throw new Error(`[ttmg-pack] plugin get error: ${JSON.stringify(json)}`);
1906
+ }
1907
+ return { url: meta.PackageCDNURL, md5: meta.PackageMD5 };
1908
+ }
1909
+ async function addDepsToPackages(gameEntry, outputDir, allDeps) {
1910
+ const gamePackConfigPath = path.join(outputDir, GAME_PACK_CONFIG_FILE_NAME);
1911
+ const gameJsonPath = path.join(gameEntry, GAME_ORIGIN_CONFIG_FILE_NAME);
1912
+ const gamePackConfig = JSON.parse(fs.readFileSync(gamePackConfigPath, 'utf-8'));
1913
+ const gameJson = JSON.parse(fs.readFileSync(gameJsonPath, 'utf-8'));
1914
+ const pluginConfig = gameJson.plugins;
1915
+ for (const [pkgName, plugins] of Object.entries(allDeps)) {
1916
+ for (const pluginName of plugins) {
1917
+ if (!(pluginConfig === null || pluginConfig === void 0 ? void 0 : pluginConfig[pluginName]))
1918
+ continue;
1919
+ const version = pluginConfig[pluginName].version;
1920
+ const pluginPkgName = `${pluginName}_${version}`;
1921
+ const { url, md5 } = await getPluginInfo({
1922
+ plugin_name: pluginName.toLowerCase(),
1923
+ plugin_version: version,
1924
+ headers: {
1925
+ 'x-tt-env': 'ppe_unity_plugin_meta',
1926
+ 'x-use-ppe': '1',
1927
+ },
1928
+ });
1929
+ gamePackConfig.packages[pkgName].dependencies.push(pluginPkgName);
1930
+ if (!gamePackConfig.packages[pluginPkgName]) {
1931
+ gamePackConfig.packages[pluginPkgName] = {
1932
+ url,
1933
+ md5,
1934
+ root: pluginPkgName,
1935
+ main: `${pluginPkgName}/index.js`,
1936
+ output: `${pluginPkgName}${STTPKG_EXT}`,
1937
+ independent: false,
1938
+ type: 'plugin',
1939
+ dependencies: [],
1940
+ };
1941
+ }
1942
+ else {
1943
+ // 如果已存在,也覆盖 url/md5,保证最新
1944
+ gamePackConfig.packages[pluginPkgName].url = url;
1945
+ gamePackConfig.packages[pluginPkgName].md5 = md5;
1946
+ }
1947
+ }
1948
+ }
1949
+ fs.writeFileSync(gamePackConfigPath, JSON.stringify(gamePackConfig, null, 2));
1950
+ }
1951
+
1782
1952
  function getSkipDirs({ packages, entryDir, }) {
1783
1953
  const skipDirs = [];
1784
1954
  if (packages) {
@@ -1791,6 +1961,7 @@ async function makePkgs({ gameEntry, gameOutput, config, }) {
1791
1961
  logger.info(`pack start,startTime:${startTime}`);
1792
1962
  const { packages } = await setup(gameEntry, gameOutput);
1793
1963
  const allDeps = collectDeps(gameEntry, packages);
1964
+ await addDepsToPackages(gameEntry, gameOutput, allDeps.pluginDeps);
1794
1965
  const allMaps = collectMaps(gameEntry, packages);
1795
1966
  const pkgOutput = {};
1796
1967
  /**
@@ -1840,6 +2011,46 @@ async function makePkgs({ gameEntry, gameOutput, config, }) {
1840
2011
  logger.info(`pack end,duration:${Date.now() - startTime}ms`);
1841
2012
  return pkgOutput;
1842
2013
  }
2014
+ async function makePlugin({ gameEntry, gameOutput, config, }) {
2015
+ try {
2016
+ const pluginConfig = fs.readFileSync(path.join(gameEntry, 'plugin.json'), 'utf-8');
2017
+ const { name, main, version } = JSON.parse(pluginConfig);
2018
+ const pluginName = `${name}_${version}`;
2019
+ // 把 entryDir 下的所有文件复制到 entryDir/plugin
2020
+ const pluginDir = path.join(gameEntry, pluginName);
2021
+ copyDirectory(gameEntry, pluginDir);
2022
+ const packages = {
2023
+ [pluginName]: {
2024
+ url: '',
2025
+ md5: '',
2026
+ root: pluginName,
2027
+ main,
2028
+ output: `${pluginName}${STTPKG_EXT}`,
2029
+ type: 'game',
2030
+ dependencies: []
2031
+ }
2032
+ };
2033
+ fs.writeFileSync(path.join(gameOutput, GAME_PACK_CONFIG_FILE_NAME), JSON.stringify({ packages }, null, 2));
2034
+ const pluginEntry = path.join(gameEntry, pluginName);
2035
+ const allMaps = collectPluginMaps(gameEntry, pluginEntry, pluginName);
2036
+ await pack({
2037
+ gameEntry,
2038
+ allDeps: {
2039
+ packageDeps: {},
2040
+ pluginDeps: {},
2041
+ },
2042
+ allMaps,
2043
+ pkgName: pluginName,
2044
+ pkgConfig: packages[pluginName],
2045
+ destRoot: path.join(gameOutput, pluginName, packages[pluginName].root),
2046
+ config,
2047
+ });
2048
+ }
2049
+ catch (error) {
2050
+ logger.error('plugin.json 不存在');
2051
+ return;
2052
+ }
2053
+ }
1843
2054
 
1844
2055
  /**
1845
2056
  * 将 packages 中的每个 package 下的文件内容合并到 outputDir 下,不保留 package name
@@ -1915,8 +2126,7 @@ async function clearPkgs(entryDir, uselessDirs = ['node_modules', '__MACOSX']) {
1915
2126
  removeSystemUseless(entryDir, uselessDirs);
1916
2127
  }
1917
2128
 
1918
- async function debugPkgs(originConfig) {
1919
- const config = overrideConfig(originConfig);
2129
+ async function debugPkgs(config) {
1920
2130
  const { entry: entryDir, output: outputDir, dev: { enableLog }, } = config;
1921
2131
  logger.init(outputDir, enableLog);
1922
2132
  try {
@@ -1962,12 +2172,16 @@ async function debugPkgs(originConfig) {
1962
2172
  }
1963
2173
  }
1964
2174
 
1965
- async function buildPkgs(originConfig) {
1966
- const config = overrideConfig(originConfig);
2175
+ async function buildPkgs(config) {
2176
+ var _a;
2177
+ if (((_a = config === null || config === void 0 ? void 0 : config.build) === null || _a === void 0 ? void 0 : _a.type) === 'plugin') {
2178
+ await buildPlugin(config);
2179
+ return;
2180
+ }
1967
2181
  const { entry: entryDir, output: outputDir, build } = config;
1968
2182
  let startTime = Date.now();
1969
2183
  logger.init(outputDir, true);
1970
- logger.info(`TTMG_PACK_VERSION: ${"0.2.8"}`);
2184
+ logger.info(`TTMG_PACK_VERSION: ${"0.2.9-beta.2"}`);
1971
2185
  logger.info(`pack start, startTime:${startTime}`);
1972
2186
  /**
1973
2187
  * 清理
@@ -1999,6 +2213,31 @@ async function buildPkgs(originConfig) {
1999
2213
  }
2000
2214
  logger.info(`pack end:${Date.now() - startTime}ms`);
2001
2215
  }
2216
+ async function buildPlugin(originConfig) {
2217
+ const { entry: entryDir, output: outputDir, build } = originConfig;
2218
+ let startTime = Date.now();
2219
+ logger.init(outputDir, true);
2220
+ logger.info(`TTMG_PACK_VERSION: ${"0.2.9-beta.2"}`);
2221
+ logger.info(`pack start, startTime:${startTime}`);
2222
+ /**
2223
+ * 打包
2224
+ */
2225
+ await makePlugin({
2226
+ config: originConfig,
2227
+ gameEntry: entryDir,
2228
+ gameOutput: outputDir,
2229
+ });
2230
+ if (build === null || build === void 0 ? void 0 : build.enableOdr) {
2231
+ /**
2232
+ * 等待文件全部读写完成后
2233
+ */
2234
+ await makeOdrPkgs(outputDir);
2235
+ /**
2236
+ * 分拆 odr 包
2237
+ */
2238
+ }
2239
+ logger.info(`pack end:${Date.now() - startTime}ms`);
2240
+ }
2002
2241
 
2003
2242
  async function getPkgs({ entryDir, }) {
2004
2243
  const independentPackages = getIndependentPackagesConfig(entryDir);