ttmg-pack 0.2.6 → 0.2.8-requirePlugin.1

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/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * ==========================================
3
3
  * @Description: ttmg pack
4
- * @Version: 0.2.6
4
+ * @Version: 0.2.8-requirePlugin.1
5
5
  * @Author: zhanghongyang.mocha
6
- * @Date: 2025-12-09 19:33:36
6
+ * @Date: 2025-12-10 19:43:32
7
7
  * ==========================================
8
8
  */
9
9
  'use strict';
@@ -500,6 +500,40 @@ async function genOpenDataContext(entryPath) {
500
500
  return jsCode;
501
501
  }
502
502
 
503
+ function copyDirectory(src, dest) {
504
+ try {
505
+ if (!fs.existsSync(dest)) {
506
+ fs.mkdirSync(dest, { recursive: true });
507
+ }
508
+ const items = fs.readdirSync(src);
509
+ const normalizedDest = path.resolve(dest) + path.sep;
510
+ items.forEach(item => {
511
+ const srcPath = path.join(src, item);
512
+ const destPath = path.join(dest, item);
513
+ // 检查当前要处理的源路径是否就是目标路径
514
+ // 这可以防止将目标目录本身复制到自身中
515
+ if (path.resolve(srcPath) === path.resolve(dest)) {
516
+ return; // 跳过目标目录本身
517
+ }
518
+ // 获取文件状态
519
+ const stat = fs.statSync(srcPath);
520
+ if (stat.isFile()) {
521
+ // 复制文件
522
+ fs.copyFileSync(srcPath, destPath);
523
+ }
524
+ else if (stat.isDirectory()) {
525
+ const childNormalizedSrc = path.resolve(srcPath) + path.sep;
526
+ if (!normalizedDest.startsWith(childNormalizedSrc)) {
527
+ copyDirectory(srcPath, destPath);
528
+ }
529
+ }
530
+ });
531
+ }
532
+ catch (error) {
533
+ logger.error(`copyDirectory error: ${error}`);
534
+ }
535
+ }
536
+
503
537
  /**
504
538
  * 将 packageConfig packages 中 分拆的包 配置迁移到 odr_packages 中
505
539
  */
@@ -519,7 +553,7 @@ async function writeOdrConfig(outputDir) {
519
553
  if (!odrPackages[relatedPkgName]) {
520
554
  odrPackages[relatedPkgName] = {};
521
555
  }
522
- odrPackages[relatedPkgName] = Object.assign(Object.assign({}, odrPackages[relatedPkgName]), { root: pkgConfig.root, code_output: pkgConfig.output, code_md5: pkgConfig.md5, main: pkgConfig.main });
556
+ 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 });
523
557
  delete packages[pkgName];
524
558
  }
525
559
  if (pkgName.endsWith(ODR_ASSET_DIR_SUFFIX)) {
@@ -548,12 +582,14 @@ async function makeOdrPkgs(outputDir) {
548
582
  const pkgOutput = path.join(outputDir, pkgName);
549
583
  const codeOutput = path.join(outputDir, `${pkgName}${ODR_CODE_DIR_SUFFIX}`);
550
584
  const assetOutput = path.join(outputDir, `${pkgName}${ODR_ASSET_DIR_SUFFIX}`);
551
- ensureDirSync(codeOutput);
552
- ensureDirSync(assetOutput);
553
- fs.cpSync(pkgOutput, codeOutput, { recursive: true });
554
- fs.cpSync(pkgOutput, assetOutput, { recursive: true });
555
- deleteNoJsFilesSync(codeOutput);
556
- deleteJsFilesSync(assetOutput);
585
+ if (packages[pkgName].type === 'game') {
586
+ ensureDirSync(codeOutput);
587
+ ensureDirSync(assetOutput);
588
+ fs.cpSync(pkgOutput, codeOutput, { recursive: true });
589
+ fs.cpSync(pkgOutput, assetOutput, { recursive: true });
590
+ deleteNoJsFilesSync(codeOutput);
591
+ deleteJsFilesSync(assetOutput);
592
+ }
557
593
  /**
558
594
  * 写入配置
559
595
  */
@@ -1240,6 +1276,8 @@ async function setup(entryDir, outputDir) {
1240
1276
  root: '',
1241
1277
  main: 'game.js',
1242
1278
  output: `${GAME_MAIN_PACKAGE_NAME}${STTPKG_EXT}`,
1279
+ type: 'game',
1280
+ dependencies: []
1243
1281
  };
1244
1282
  transformSubPackages(gameJson.subpackages).forEach(({ name, root, main, independent }) => {
1245
1283
  // root 是目录
@@ -1250,6 +1288,8 @@ async function setup(entryDir, outputDir) {
1250
1288
  main,
1251
1289
  output: `${name}${STTPKG_EXT}`,
1252
1290
  independent,
1291
+ type: 'game',
1292
+ dependencies: []
1253
1293
  };
1254
1294
  if (independent) {
1255
1295
  fyfPackages.push({
@@ -1397,6 +1437,27 @@ function collectMaps(entryDir, packages) {
1397
1437
  logger.info('基于游戏源代码收集分包中的 JS 文件完成');
1398
1438
  return result;
1399
1439
  }
1440
+ function collectPluginMaps(gameDir, entryDir, name) {
1441
+ logger.info('开始基于插件源代码收集分包中的 JS 文件');
1442
+ const result = {};
1443
+ let jsFiles;
1444
+ jsFiles = collectPkgJsFiles({
1445
+ entry: entryDir,
1446
+ root: entryDir,
1447
+ exts: ['.js', '.ts', '.jsx', '.tsx'],
1448
+ excludeDirs: [],
1449
+ });
1450
+ const list = [];
1451
+ jsFiles.forEach(absPath => {
1452
+ list.push(path.relative(gameDir, absPath).replace(/\\/g, '/'));
1453
+ });
1454
+ const packNameRoot = name;
1455
+ result[name] = {
1456
+ [`${packNameRoot}/game.pack.js`]: list,
1457
+ };
1458
+ logger.info('基于插件源代码收集分包中的 JS 文件完成');
1459
+ return result;
1460
+ }
1400
1461
 
1401
1462
  function collectDeps(gameEntry, packages) {
1402
1463
  // 自动扫描 game 目录下的一级文件夹作为根前缀
@@ -1449,6 +1510,7 @@ function collectDeps(gameEntry, packages) {
1449
1510
  }
1450
1511
  requireCalls.push({
1451
1512
  file: path.relative(gameEntry, fullPath),
1513
+ type: 'game',
1452
1514
  callee: node.callee.name,
1453
1515
  requirePath,
1454
1516
  resolvedPath: resolvedPath
@@ -1456,6 +1518,20 @@ function collectDeps(gameEntry, packages) {
1456
1518
  : '',
1457
1519
  });
1458
1520
  }
1521
+ else if (node.type === 'CallExpression' &&
1522
+ node.callee.type === 'Identifier' &&
1523
+ node.callee.name === 'requirePlugin' &&
1524
+ node.arguments.length > 0 &&
1525
+ node.arguments[0].type === 'Literal') {
1526
+ const requirePath = node.arguments[0].value;
1527
+ requireCalls.push({
1528
+ file: path.relative(gameEntry, fullPath),
1529
+ type: 'plugin',
1530
+ callee: node.callee.name,
1531
+ requirePath,
1532
+ resolvedPath: requirePath,
1533
+ });
1534
+ }
1459
1535
  });
1460
1536
  });
1461
1537
  const newRequireCalls = requireCalls.map(i => {
@@ -1463,11 +1539,16 @@ function collectDeps(gameEntry, packages) {
1463
1539
  * 基于 packages 来判断
1464
1540
  */
1465
1541
  let depModule = '';
1466
- Object.keys(packages).forEach(pkg => {
1467
- if (i.resolvedPath && i.resolvedPath.startsWith(packages[pkg].root)) {
1468
- depModule = pkg;
1469
- }
1470
- });
1542
+ if (i.type === 'plugin') {
1543
+ depModule = i.requirePath.split('/')[0];
1544
+ }
1545
+ else if (i.type === 'game') {
1546
+ Object.keys(packages).forEach(pkg => {
1547
+ if (i.resolvedPath && i.resolvedPath.startsWith(packages[pkg].root)) {
1548
+ depModule = pkg;
1549
+ }
1550
+ });
1551
+ }
1471
1552
  let curModule = '';
1472
1553
  Object.keys(packages).forEach(pkg => {
1473
1554
  if (i.file.startsWith(packages[pkg].root)) {
@@ -1481,17 +1562,26 @@ function collectDeps(gameEntry, packages) {
1481
1562
  const exts = ['.js', '.ts', '.jsx', '.tsx'];
1482
1563
  const isJsLike = f => exts.some(ext => f.endsWith(ext));
1483
1564
  const result = newRequireCalls.reduce((acc, cur) => {
1484
- if (!acc[cur.curModule]) {
1485
- acc[cur.curModule] = {};
1565
+ if (!acc.packageDeps[cur.curModule]) {
1566
+ acc.packageDeps[cur.curModule] = {};
1567
+ }
1568
+ if (!acc.pluginDeps[cur.curModule]) {
1569
+ acc.pluginDeps[cur.curModule] = [];
1486
1570
  }
1487
1571
  if (cur.curModule === cur.depModule) {
1488
1572
  return acc;
1489
1573
  }
1490
- if (cur.resolvedPath && isJsLike(cur.resolvedPath)) {
1491
- acc[cur.curModule] = Object.assign(Object.assign({}, acc[cur.curModule]), { [cur.resolvedPath]: cur.depModule });
1574
+ if (cur.resolvedPath && isJsLike(cur.resolvedPath) && cur.type === 'game') {
1575
+ acc.packageDeps[cur.curModule][cur.resolvedPath] = cur.depModule;
1576
+ }
1577
+ else if (cur.type === 'plugin') {
1578
+ acc.pluginDeps[cur.curModule].push(cur.depModule);
1492
1579
  }
1493
1580
  return acc;
1494
- }, {});
1581
+ }, {
1582
+ packageDeps: {},
1583
+ pluginDeps: {},
1584
+ });
1495
1585
  return result;
1496
1586
  }
1497
1587
  function getRootPrefixes(gameEntry) {
@@ -1537,10 +1627,19 @@ async function pack({ gameEntry, pkgName, allDeps, allMaps, pkgConfig, destRoot,
1537
1627
  const packMap = allMaps[pkgName];
1538
1628
  const moduleConfig = {
1539
1629
  main: pkgConfig.main,
1540
- deps: allDeps[pkgName] || {},
1630
+ deps: allDeps.packageDeps[pkgName] || {},
1541
1631
  map: allMaps[pkgName],
1542
1632
  };
1543
1633
  ensureDirSync(destRoot);
1634
+ /**
1635
+ * 把 openDataContext 代码放到 destRoot 下
1636
+ */
1637
+ if (pkgName === GAME_MAIN_PACKAGE_NAME) {
1638
+ await buildOpenDataContext({
1639
+ entryDir: gameEntry,
1640
+ outputDir: destRoot,
1641
+ });
1642
+ }
1544
1643
  fs.writeFileSync(path.join(destRoot, GAME_MODULE_CONFIG_FILE_NAME), JSON.stringify(moduleConfig));
1545
1644
  for (const packFileName in packMap) {
1546
1645
  const relPaths = packMap[packFileName];
@@ -1579,7 +1678,8 @@ async function pack({ gameEntry, pkgName, allDeps, allMaps, pkgConfig, destRoot,
1579
1678
  // });
1580
1679
  // const code = fs.readFileSync(absPath, 'utf-8');
1581
1680
  const fileId = relPath.replace(/\\/g, '/');
1582
- const defineHeader = `define("game:${fileId}", ["require", "requireAsync", "module", "exports", "sandboxGlobal"], function(require, requireAsync, module, exports, sandboxGlobal){\nwith(sandboxGlobal){\n`;
1681
+ const schema = config.build.type === 'plugin' ? 'plugin' : 'game';
1682
+ const defineHeader = `define("${schema}:${fileId}", ["require", "requireAsync", "module", "exports", "sandboxGlobal"], function(require, requireAsync, module, exports, sandboxGlobal){\nwith(sandboxGlobal){\n`;
1583
1683
  const defineFooter = `\n}\n});\n`;
1584
1684
  const fileMagic = new MagicString(code, { filename: fileId });
1585
1685
  fileMagic.prepend(defineHeader);
@@ -1666,6 +1766,35 @@ async function partition({ pkgRoot, destRoot, packedFiles, gameEntry, gameOutput
1666
1766
  }
1667
1767
  }
1668
1768
 
1769
+ async function addDepsToPackages(gameEntry, outputDir, allDeps) {
1770
+ const gamePackConfigPath = path.join(outputDir, GAME_PACK_CONFIG_FILE_NAME);
1771
+ const gameJsonPath = path.join(gameEntry, GAME_ORIGIN_CONFIG_FILE_NAME);
1772
+ const gamePackConfig = JSON.parse(fs.readFileSync(gamePackConfigPath, 'utf-8'));
1773
+ const gameJson = JSON.parse(fs.readFileSync(gameJsonPath, 'utf-8'));
1774
+ const pluginConfig = gameJson.plugins;
1775
+ Object.entries(allDeps).forEach(([pkgName, plugins]) => {
1776
+ plugins.forEach(pluginName => {
1777
+ if (pluginConfig[pluginName]) {
1778
+ const version = pluginConfig[pluginName].version;
1779
+ gamePackConfig.packages[pkgName].dependencies.push(`${pluginName}_${version}`);
1780
+ if (!gamePackConfig.packages[`${pluginName}_${version}`]) {
1781
+ gamePackConfig.packages[`${pluginName}_${version}`] = {
1782
+ url: '',
1783
+ md5: '',
1784
+ root: '',
1785
+ main: '',
1786
+ output: `${pluginName}_${version}${STTPKG_EXT}`,
1787
+ independent: false,
1788
+ type: 'plugin',
1789
+ dependencies: []
1790
+ };
1791
+ }
1792
+ }
1793
+ });
1794
+ });
1795
+ fs.writeFileSync(gamePackConfigPath, JSON.stringify(gamePackConfig, null, 2));
1796
+ }
1797
+
1669
1798
  function getSkipDirs({ packages, entryDir, }) {
1670
1799
  const skipDirs = [];
1671
1800
  if (packages) {
@@ -1683,6 +1812,7 @@ async function makePkgs({ gameEntry, gameOutput, config, }) {
1683
1812
  logger.info(`pack start,startTime:${startTime}`);
1684
1813
  const { packages } = await setup(gameEntry, gameOutput);
1685
1814
  const allDeps = collectDeps(gameEntry, packages);
1815
+ await addDepsToPackages(gameEntry, gameOutput, allDeps.pluginDeps);
1686
1816
  const allMaps = collectMaps(gameEntry, packages);
1687
1817
  const pkgOutput = {};
1688
1818
  /**
@@ -1724,18 +1854,54 @@ async function makePkgs({ gameEntry, gameOutput, config, }) {
1724
1854
  });
1725
1855
  /**
1726
1856
  * build open data context
1727
- */
1857
+ */
1728
1858
  logger.info(`开始基于开放数据域源代码打包`);
1729
1859
  startTime = Date.now();
1730
- await buildOpenDataContext({
1731
- entryDir: gameEntry,
1732
- outputDir: gameOutput,
1733
- });
1734
1860
  logger.info(`基于开放数据域源代码打包完成`);
1735
1861
  logger.info(`基于开放数据域源代码打包耗时,耗时:${Date.now() - startTime}ms`);
1736
1862
  logger.info(`pack end,duration:${Date.now() - startTime}ms`);
1737
1863
  return pkgOutput;
1738
1864
  }
1865
+ async function makePlugin({ gameEntry, gameOutput, config, }) {
1866
+ try {
1867
+ const pluginConfig = fs.readFileSync(path.join(gameEntry, 'plugin.json'), 'utf-8');
1868
+ const { name, main, version } = JSON.parse(pluginConfig);
1869
+ const pluginName = `${name}_${version}`;
1870
+ // 把 entryDir 下的所有文件复制到 entryDir/plugin
1871
+ const pluginDir = path.join(gameEntry, pluginName);
1872
+ copyDirectory(gameEntry, pluginDir);
1873
+ const packages = {
1874
+ [pluginName]: {
1875
+ url: '',
1876
+ md5: '',
1877
+ root: pluginName,
1878
+ main,
1879
+ output: `${pluginName}${STTPKG_EXT}`,
1880
+ type: 'game',
1881
+ dependencies: []
1882
+ }
1883
+ };
1884
+ fs.writeFileSync(path.join(gameOutput, GAME_PACK_CONFIG_FILE_NAME), JSON.stringify({ packages }, null, 2));
1885
+ const pluginEntry = path.join(gameEntry, pluginName);
1886
+ const allMaps = collectPluginMaps(gameEntry, pluginEntry, pluginName);
1887
+ await pack({
1888
+ gameEntry,
1889
+ allDeps: {
1890
+ packageDeps: {},
1891
+ pluginDeps: {},
1892
+ },
1893
+ allMaps,
1894
+ pkgName: pluginName,
1895
+ pkgConfig: packages[pluginName],
1896
+ destRoot: path.join(gameOutput, pluginName, packages[pluginName].root),
1897
+ config,
1898
+ });
1899
+ }
1900
+ catch (error) {
1901
+ logger.error('plugin.json 不存在');
1902
+ return;
1903
+ }
1904
+ }
1739
1905
 
1740
1906
  /**
1741
1907
  * 将 packages 中的每个 package 下的文件内容合并到 outputDir 下,不保留 package name
@@ -1851,10 +2017,15 @@ async function debugPkgs(originConfig) {
1851
2017
  }
1852
2018
 
1853
2019
  async function buildPkgs(originConfig) {
2020
+ var _a;
2021
+ if (((_a = originConfig === null || originConfig === void 0 ? void 0 : originConfig.build) === null || _a === void 0 ? void 0 : _a.type) === 'plugin') {
2022
+ await buildPlugin(originConfig);
2023
+ return;
2024
+ }
1854
2025
  const { entry: entryDir, output: outputDir, build } = originConfig;
1855
2026
  let startTime = Date.now();
1856
2027
  logger.init(outputDir, true);
1857
- logger.info(`TTMG_PACK_VERSION: ${"0.2.6"}`);
2028
+ logger.info(`TTMG_PACK_VERSION: ${"0.2.8-requirePlugin.1"}`);
1858
2029
  logger.info(`pack start, startTime:${startTime}`);
1859
2030
  const config = overrideConfig(entryDir, originConfig);
1860
2031
  /**
@@ -1883,6 +2054,31 @@ async function buildPkgs(originConfig) {
1883
2054
  }
1884
2055
  logger.info(`pack end:${Date.now() - startTime}ms`);
1885
2056
  }
2057
+ async function buildPlugin(originConfig) {
2058
+ const { entry: entryDir, output: outputDir, build } = originConfig;
2059
+ let startTime = Date.now();
2060
+ logger.init(outputDir, true);
2061
+ logger.info(`TTMG_PACK_VERSION: ${"0.2.8-requirePlugin.1"}`);
2062
+ logger.info(`pack start, startTime:${startTime}`);
2063
+ /**
2064
+ * 打包
2065
+ */
2066
+ await makePlugin({
2067
+ config: originConfig,
2068
+ gameEntry: entryDir,
2069
+ gameOutput: outputDir,
2070
+ });
2071
+ if (build === null || build === void 0 ? void 0 : build.enableOdr) {
2072
+ /**
2073
+ * 等待文件全部读写完成后
2074
+ */
2075
+ await makeOdrPkgs(outputDir);
2076
+ /**
2077
+ * 分拆 odr 包
2078
+ */
2079
+ }
2080
+ logger.info(`pack end:${Date.now() - startTime}ms`);
2081
+ }
1886
2082
 
1887
2083
  async function getPkgs({ entryDir, }) {
1888
2084
  const independentPackages = getIndependentPackagesConfig(entryDir);