@dd-code/uni-tools 1.0.13 → 1.0.15

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.mjs.js CHANGED
@@ -273,6 +273,11 @@ var unlinkDeepDirOrFile = function (filePath) {
273
273
  console.error("删除文件夹失败:", err);
274
274
  }
275
275
  };
276
+ var getFilePathWithoutExt = function (filePath) {
277
+ var dirName = path.dirname(filePath);
278
+ var baseName = path.basename(filePath, path.extname(filePath));
279
+ return path.join(dirName, baseName);
280
+ };
276
281
 
277
282
  var UniCdnManager = /** @class */ (function () {
278
283
  function UniCdnManager() {
@@ -622,6 +627,11 @@ var downloadProjectFiles = function (manifestList) { return __awaiter(void 0, vo
622
627
  });
623
628
  }); };
624
629
 
630
+ /**
631
+ * 计算需要下载/更新的 Manifest 列表
632
+ * - 比较 node_modules 缓存中的 manifest 哈希与远端 manifest 哈希
633
+ * - 返回需要更新的清单(用于后续下载项目文件到本地缓存)
634
+ */
625
635
  var checkDownloadFilesIsExpired = function (manifestList) {
626
636
  var filterList = manifestList.filter(function (conf) {
627
637
  var targetPath = getNodeModulesEnvAppCodeFilePath(conf, MANIFEST_NAME);
@@ -631,6 +641,14 @@ var checkDownloadFilesIsExpired = function (manifestList) {
631
641
  });
632
642
  return filterList;
633
643
  };
644
+ /**
645
+ * Manifest 管理器(构建态/运行态共享状态)
646
+ * - value:当前应用 Manifest(会随构建过程逐步填充)
647
+ * - setEnv:读取 cli/vite 环境,设置 isRoot/isServe/code/appCode/publicPath 等
648
+ * - setPagesJson/setFiles/setExposes:在插件流程中填充 pages 与产物清单与运行时暴露
649
+ * - saveFile:落盘 Manifest(计算 hash,剔除临时字段 isServe)
650
+ * - dependencies:记录其他应用的 Manifest(用于主应用合并)
651
+ */
634
652
  var createManifestManager = function () {
635
653
  var envObj = process.env;
636
654
  var row = {
@@ -659,6 +677,9 @@ var createManifestManager = function () {
659
677
  newManifest.hash = generateSHA256(JSON.stringify(__assign({}, newManifest)));
660
678
  writeFiles(filePath, JSON.stringify(newManifest, null, 2));
661
679
  },
680
+ setExposes: function (exposes) {
681
+ row.exposes = exposes || {};
682
+ },
662
683
  setEnv: function (mode) {
663
684
  var env = formatCliCommandConfig(mode);
664
685
  var mfeJson = getMfeJson();
@@ -680,8 +701,93 @@ var createManifestManager = function () {
680
701
  },
681
702
  };
682
703
  };
704
+ /**
705
+ * 从 CDN 获取主应用 Manifest(ROOT_APP_CODE)
706
+ * - 仅用于校验/兜底:优先使用本地 dist 或缓存 node_modules 的 Manifest
707
+ */
708
+ var downloadMainAppManifestJson = function (currentManifest) { return __awaiter(void 0, void 0, void 0, function () {
709
+ var mode, code, cdnUrl, manifestJson;
710
+ return __generator(this, function (_b) {
711
+ switch (_b.label) {
712
+ case 0:
713
+ mode = currentManifest.mode, code = currentManifest.code;
714
+ _b.label = 1;
715
+ case 1:
716
+ _b.trys.push([1, 3, , 4]);
717
+ cdnUrl = cdn.getManifestUrl({
718
+ code: code,
719
+ appCode: ROOT_APP_CODE,
720
+ mode: mode,
721
+ });
722
+ return [4 /*yield*/, fetchFileByPath(cdnUrl)];
723
+ case 2:
724
+ manifestJson = _b.sent();
725
+ return [2 /*return*/, manifestJson];
726
+ case 3:
727
+ _b.sent();
728
+ return [3 /*break*/, 4];
729
+ case 4: return [2 /*return*/, {}];
730
+ }
731
+ });
732
+ }); };
733
+ /**
734
+ * 读取根主应用 Manifest(优先级:dist > node_modules 缓存 > CDN)
735
+ * - 兼容不同构建场景:
736
+ * - isServe(WS 联调)或内部构建(inner build)下无需校验哈希
737
+ * - 其他场景严格校验本地与远端 Manifest 的 hash 一致性
738
+ * - 选择顺序:
739
+ * 1) 当前构建输出目录的 app.json(dist)
740
+ * 2) node_modules 缓存目录中的 app.json(save cdn file path)
741
+ * 3) 远端 CDN 拉取的 Manifest(兜底)
742
+ */
743
+ var getRootMainManifestJson = function (currentManifest) { return __awaiter(void 0, void 0, void 0, function () {
744
+ var mode, isInnerBuild, isWsBuild, needCheckHash, originHostManifestJson, checkHash, distPagePath, pageManifest, nodeModulePath, nodeModulesManifest;
745
+ return __generator(this, function (_a) {
746
+ switch (_a.label) {
747
+ case 0:
748
+ mode = currentManifest.mode;
749
+ isInnerBuild = checkIsInnerBuild();
750
+ isWsBuild = currentManifest.isServe;
751
+ needCheckHash = !(isWsBuild || isInnerBuild);
752
+ return [4 /*yield*/, downloadMainAppManifestJson(currentManifest)];
753
+ case 1:
754
+ originHostManifestJson = _a.sent();
755
+ checkHash = function (target) {
756
+ if ((originHostManifestJson === null || originHostManifestJson === void 0 ? void 0 : originHostManifestJson.hash) && needCheckHash && (originHostManifestJson === null || originHostManifestJson === void 0 ? void 0 : originHostManifestJson.hash) !== target.hash) {
757
+ throw new Error("originHostManifestJson hash not equal");
758
+ }
759
+ };
760
+ try {
761
+ distPagePath = path.join(process.env.UNI_OUTPUT_DIR, MANIFEST_NAME);
762
+ pageManifest = uniReadFile(distPagePath);
763
+ checkHash(pageManifest);
764
+ console.log("distPagePath done");
765
+ if (Object.keys(pageManifest).length !== 0) {
766
+ return [2 /*return*/, pageManifest];
767
+ }
768
+ }
769
+ catch (_b) { }
770
+ try {
771
+ nodeModulePath = path.join(SAVE_CDN_FILE_PATH, mode, ROOT_APP_CODE, MANIFEST_NAME);
772
+ nodeModulesManifest = uniReadFile(nodeModulePath);
773
+ checkHash(nodeModulesManifest);
774
+ console.log("nodeModulePath done");
775
+ if (Object.keys(nodeModulesManifest).length !== 0) {
776
+ return [2 /*return*/, nodeModulesManifest];
777
+ }
778
+ }
779
+ catch (_c) { }
780
+ console.log('origin done');
781
+ return [2 /*return*/, originHostManifestJson];
782
+ }
783
+ });
784
+ }); };
683
785
 
684
786
  var num = 5;
787
+ /**
788
+ * 从主应用 HTTP 服务拉取当前进程环境(含 UNI_OUTPUT_DIR)
789
+ * - serve 非联调模式下,子应用通过此接口获知主应用输出根目录
790
+ */
685
791
  var getMainProcess = function () { return __awaiter(void 0, void 0, void 0, function () {
686
792
  var URL, mainProcess, result;
687
793
  return __generator(this, function (_a) {
@@ -699,6 +805,10 @@ var getMainProcess = function () { return __awaiter(void 0, void 0, void 0, func
699
805
  }
700
806
  });
701
807
  }); };
808
+ /**
809
+ * 简易重试(最多 5 次,每次 100ms)
810
+ * - 主应用 HTTP 服务启动可能有延迟,子应用循环尝试获取
811
+ */
702
812
  var getMainProcessLoop = function () { return __awaiter(void 0, void 0, void 0, function () {
703
813
  var mainProcess;
704
814
  return __generator(this, function (_a) {
@@ -754,6 +864,10 @@ var resetOutDir = function (currentManifestJson, config) { return __awaiter(void
754
864
  process.env.MFE_ROOT_OUTPUT_DIR = (_a = process.env.MFE_SOURCE_OUTPUT_DIR) === null || _a === void 0 ? void 0 : _a.replace(exp, "/").replace(/\/$/, "");
755
865
  // if (!currentManifestJson.value.isRoot)
756
866
  // console.log(config);
867
+ /**
868
+ * 非内部构建下,将 UNI_OUTPUT_DIR 统一指向主应用根输出目录
869
+ * - 便于后续写入 app.json 与拷贝产物
870
+ */
757
871
  if (!checkIsInnerBuild()) {
758
872
  // setTimeout(() => {
759
873
  process.env.UNI_OUTPUT_DIR = process.env.MFE_ROOT_OUTPUT_DIR;
@@ -764,6 +878,11 @@ var resetOutDir = function (currentManifestJson, config) { return __awaiter(void
764
878
  });
765
879
  }); };
766
880
 
881
+ /**
882
+ * 接管 uni 内置复制钩子
883
+ * - 通过 before/after 钩子记录从源到目标的拷贝行为
884
+ * - 便于在 Manifest 插件中收集运行期拷贝产生的文件清单
885
+ */
767
886
  var addUniCopyPluginHook = function (_a) {
768
887
  var before = _a.before, after = _a.after;
769
888
  var FileWatcher = require("@dcloudio/uni-cli-shared/dist/watcher").FileWatcher;
@@ -785,6 +904,11 @@ var copyFilesByTargetPath = function (sourcePath, targetPath) {
785
904
  fsExtra.ensureDirSync(targetDir);
786
905
  }
787
906
  catch (err) { }
907
+ /**
908
+ * 内容比较:
909
+ * - 仅当源/目标均为文件且内容完全一致时跳过,避免无效写入
910
+ * - 目录或不存在的目标不跳过
911
+ */
788
912
  var shouldSkip = function () {
789
913
  try {
790
914
  var s = fs.statSync(sourcePath);
@@ -793,6 +917,11 @@ var copyFilesByTargetPath = function (sourcePath, targetPath) {
793
917
  return false;
794
918
  var sb = fs.readFileSync(sourcePath);
795
919
  var tb = fs.readFileSync(targetPath);
920
+ /**
921
+ * Buffer 比较:
922
+ * - 长度与内容完全一致时跳过
923
+ * - 保留二进制级别比较,适配图片/字体等非文本文件
924
+ */
796
925
  return sb.length === tb.length && sb.equals(tb);
797
926
  }
798
927
  catch (_a) {
@@ -881,6 +1010,13 @@ var initPrePagesJson = function () {
881
1010
  var pagesPath = path.resolve(process.cwd(), "src/pages.json");
882
1011
  return fs.readFileSync(pagesPath, "utf-8");
883
1012
  };
1013
+ /**
1014
+ * 解析并预处理 pages.json
1015
+ * - 读取全局 preprocess 上下文(根据平台注入不同变量)
1016
+ * - 使用 @dcloudio 的 preprocess 对 pages.json 做条件编译(如 #ifdef/#endif)
1017
+ * - 返回 JS 对象形式的 pages 配置
1018
+ * - isFormat=true 且平台为 mp-weixin 时,转换为微信特定格式(去除 globalStyle 并映射到 window/subPackages)
1019
+ */
884
1020
  var getPagesJson = function (jsonFile, isFormat) {
885
1021
  if (isFormat === void 0) { isFormat = false; }
886
1022
  var platform = process.env.UNI_PLATFORM;
@@ -893,6 +1029,12 @@ var getPagesJson = function (jsonFile, isFormat) {
893
1029
  }
894
1030
  return pagesConfig;
895
1031
  };
1032
+ /**
1033
+ * 将 Uni 的 pages.json 转换为微信小程序格式
1034
+ * - window:映射 globalStyle,并兼容 mp-weixin 特定字段
1035
+ * - subPackages:后续会在 app-json.ts 中由各子应用补充到主应用
1036
+ * - usingComponents:保留全局组件声明
1037
+ */
896
1038
  var transformToWeixinFormat = function (uniPagesConfig) {
897
1039
  var _a;
898
1040
  var weixinConfig = __assign({ pages: [], window: {}, tabBar: null, subPackages: [], usingComponents: {} }, uniPagesConfig);
@@ -958,6 +1100,19 @@ var transformToWeixinFormat = function (uniPagesConfig) {
958
1100
  };
959
1101
 
960
1102
  // import { addRunningAppToSave } from "../running-core";
1103
+ /**
1104
+ * Manifest 采集与产物清单插件
1105
+ * - 目标:收集构建阶段产物与运行期拷贝的文件列表,生成可供主应用消费的文件清单
1106
+ * - CollectFiles:
1107
+ * - emitted:bundle 产物与拷贝文件的统一索引(fileName -> fileUrl)
1108
+ * - callbackFiles:拦截 uni 的复制钩子,收集被拷贝的源与目标相对路径
1109
+ * - copyFilesToPublishDir:将 outDir 下的产物拷贝到 publish 目录,便于后续上载/分发
1110
+ * - 插件钩子:
1111
+ * - config:记录 outDir,拦截 uni 拷贝行为
1112
+ * - renderStart:解析 pages.json,写入到 Manifest 状态
1113
+ * - writeBundle:收集本次打包输出的 chunk 信息
1114
+ * - closeBundle:汇总拷贝与产物,写入 Manifest 文件并拷贝到发布目录
1115
+ */
961
1116
  var CollectFiles = /** @class */ (function () {
962
1117
  function CollectFiles() {
963
1118
  this.callbackFiles = [];
@@ -977,6 +1132,11 @@ var CollectFiles = /** @class */ (function () {
977
1132
  Object.keys(bundles).forEach(function (key) {
978
1133
  var boundle = bundles[key];
979
1134
  var fileName = boundle.fileName, source = boundle.source, code = boundle.code;
1135
+ /**
1136
+ * code/source:
1137
+ * - chunk 使用 code 字段,asset 使用 source 字段
1138
+ * - 统一抽象为 fileInfo 行(包含 fileUrl 与大小哈希)
1139
+ */
980
1140
  var fileInfo = genreFileInfoRow({ fileName: fileName, source: code || source });
981
1141
  _this.emitted.set(fileName, fileInfo);
982
1142
  });
@@ -1071,6 +1231,12 @@ var createManifestPlugin = function (manifestJson) {
1071
1231
  };
1072
1232
  };
1073
1233
 
1234
+ /**
1235
+ * 主应用 app.json 渲染与写入
1236
+ * - renderPagesJsonByArray:将多个子应用的 pages.json 以分包形式合入主应用
1237
+ * - genreFullMainAppJsonByManifestList:按 Manifest 列表生成完整的主应用 app.json
1238
+ * - genreNewAppJson:内容比较后写入 app.json,避免无效写入导致的频繁重启
1239
+ */
1074
1240
  var renderPagesJsonByArray = function (appPages, pageJson) {
1075
1241
  var _loop_1 = function (app) {
1076
1242
  var _a = getPagesJson(JSON.stringify((app === null || app === void 0 ? void 0 : app.pagesJson) || {}), true).pages, pages = _a === void 0 ? [] : _a;
@@ -1104,6 +1270,14 @@ var genreNewAppJson = function (outputPageJsonPath, appJson, manifestList) {
1104
1270
  }
1105
1271
  };
1106
1272
 
1273
+ /**
1274
+ * 运行期联调核心
1275
+ * - getAppsManifestList:在主应用非联调构建模式下,返回所有子应用的 Manifest 路径列表
1276
+ * - startDistWatcher:监听子应用输出目录的父级变更,增量拷贝到主应用分包路径,并上报变更
1277
+ * - 设计要点:
1278
+ * - 监听父级目录以覆盖新增/删除场景;通过 `isTargetFile` 过滤目标变化
1279
+ * - 拷贝时进行内容比较,避免触发无效写入
1280
+ */
1107
1281
  var getAppsManifestList = function (mode) {
1108
1282
  var _a;
1109
1283
  var manifest = formatCliCommandConfig(mode);
@@ -1133,6 +1307,11 @@ var startDistWatcher = function (mainPwd, onReady, onChange) {
1133
1307
  var filePath = path.resolve(root);
1134
1308
  checkAndgenreDir(filePath);
1135
1309
  var parentDir = path.dirname(filePath);
1310
+ /**
1311
+ * 监听父级目录:
1312
+ * - uni 输出目录在构建时可能新增/删除文件或目录,监听父级可捕捉到此类变化
1313
+ * - 通过 isTargetFile 精确过滤与目标目录相关的事件
1314
+ */
1136
1315
  var watcher = createFileWatcher(parentDir);
1137
1316
  var isTargetFile = function (p) {
1138
1317
  return p.startsWith(filePath + path.sep) || p === filePath;
@@ -1147,6 +1326,11 @@ var startDistWatcher = function (mainPwd, onReady, onChange) {
1147
1326
  var sourcePath = p;
1148
1327
  var targetPath = path.join(mainPwd, rel);
1149
1328
  if (["add", "change"].includes(evt)) {
1329
+ /**
1330
+ * 增量拷贝:
1331
+ * - 目录结构保持一致,从子应用输出拷贝到主应用分包路径
1332
+ * - 通过 copyFilesByTargetPath 的内容比较避免重复写入
1333
+ */
1150
1334
  copyFilesByTargetPath(sourcePath, targetPath);
1151
1335
  }
1152
1336
  onChange === null || onChange === void 0 ? void 0 : onChange({ type: evt, p: p, sourcePath: sourcePath, targetPath: targetPath });
@@ -1155,6 +1339,13 @@ var startDistWatcher = function (mainPwd, onReady, onChange) {
1155
1339
  };
1156
1340
 
1157
1341
  var app = null;
1342
+ /**
1343
+ * 创建开发态 HTTP 服务
1344
+ * - 单例复用:避免重复创建与重复监听
1345
+ * - 路由:
1346
+ * - GET `${HTTP_PATH}/root-path`:返回当前进程的环境变量(主应用输出位置等)
1347
+ * - 与 WS 服务配合:WSServer 绑定同一个 HTTP server 进行升级
1348
+ */
1158
1349
  var createHttpServer = function () {
1159
1350
  if (app)
1160
1351
  return { server: app, start: function () { } };
@@ -1164,6 +1355,11 @@ var createHttpServer = function () {
1164
1355
  res.send(process.env);
1165
1356
  });
1166
1357
  app.use(cors({ origin: "*" }));
1358
+ /**
1359
+ * 返回:
1360
+ * - server:HTTP Server 实例(供 WS 复用)
1361
+ * - start(type):启动监听并输出日志,type 用于日志区分('http' | 'ws')
1362
+ */
1167
1363
  return {
1168
1364
  server: server,
1169
1365
  start: function (type) {
@@ -1181,6 +1377,10 @@ var WsServer = /** @class */ (function () {
1181
1377
  this.httpServer = createHttpServer();
1182
1378
  this.clientMap = new Map();
1183
1379
  }
1380
+ /**
1381
+ * 单例获取 WS 服务实例
1382
+ * - 若已创建则复用并更新消息回调
1383
+ */
1184
1384
  WsServer.getInstance = function (handleMessage) {
1185
1385
  if (!WsServer.instance) {
1186
1386
  WsServer.instance = new WsServer(handleMessage);
@@ -1208,12 +1408,18 @@ var WsServer = /** @class */ (function () {
1208
1408
  this.handleMessage = callback;
1209
1409
  }
1210
1410
  };
1411
+ /**
1412
+ * 向指定 appCode 的客户端发送消息
1413
+ */
1211
1414
  WsServer.prototype.sendMessageToApp = function (appCode, message) {
1212
1415
  var ws = this.clientMap.get(appCode);
1213
1416
  if (ws) {
1214
1417
  ws.send(typeof message === "string" ? message : JSON.stringify(message));
1215
1418
  }
1216
1419
  };
1420
+ /**
1421
+ * 连接事件:建立客户端映射并下发初始化信息(主应用输出目录)
1422
+ */
1217
1423
  WsServer.prototype.onConnection = function () {
1218
1424
  var _this = this;
1219
1425
  this.wss.on("connection", function (ws, request) {
@@ -1252,6 +1458,9 @@ var WsClientServer = /** @class */ (function () {
1252
1458
  this.ws = null;
1253
1459
  this.isConnected = false;
1254
1460
  }
1461
+ /**
1462
+ * 获取客户端单例
1463
+ */
1255
1464
  WsClientServer.getInstance = function (handleMessage) {
1256
1465
  if (!WsClientServer.instance) {
1257
1466
  WsClientServer.instance = new WsClientServer(handleMessage);
@@ -1305,6 +1514,9 @@ var WsClientServer = /** @class */ (function () {
1305
1514
  this.handleMessage = callback;
1306
1515
  }
1307
1516
  };
1517
+ /**
1518
+ * 简单重试策略:一定时间后重新连接
1519
+ */
1308
1520
  WsClientServer.prototype.retryConnect = function (appCode) {
1309
1521
  var _this = this;
1310
1522
  if (this.isConnected)
@@ -1329,6 +1541,20 @@ var WsClientServer = /** @class */ (function () {
1329
1541
  // export const mfeServer = new WsServer();
1330
1542
  // export const mfeClientServer = new WsClientServer();
1331
1543
 
1544
+ /**
1545
+ * 主应用插件(Main App)
1546
+ * - 服务端(根应用):
1547
+ * - 启动 WS + HTTP 服务,向子应用广播初始化信息(主应用输出目录)
1548
+ * - 接收子应用文件变更事件,拉取对应子应用 Manifest 并增量更新主应用 app.json
1549
+ * - 客户端(子应用):
1550
+ * - 连接主应用 WS,接收初始化 pwd 后将自身构建产物增量拷贝到主应用分包路径
1551
+ * - 在构建完成后启动本地 DistWatcher,监控自身输出变更并通过 WS 通知主应用
1552
+ * - 构建阶段:
1553
+ * - 合并所有依赖子应用的 pages.json,生成最终主应用 app.json,内容比较避免无效写入
1554
+ * - 关键点:
1555
+ * - `isBuild`/`isServe`/`isRoot` 三态控制插件行为,避免生产与联调逻辑相互影响
1556
+ * - `copyFilesByTargetPath` 与 `genreNewAppJson` 均内置内容比较,防止频繁重启
1557
+ */
1332
1558
  var filterManifestJsonListAndMainPageJson = function (manifestJsonList) {
1333
1559
  var outputPageJsonPath = getMainAppJsonPath();
1334
1560
  return {
@@ -1380,6 +1606,11 @@ var createMainAppPlugin = function (manifestJson) {
1380
1606
  startDistWatcher(state.mainPwd, function () {
1381
1607
  state.isWatcherReady = true;
1382
1608
  }, function (change) {
1609
+ /**
1610
+ * 通过 WS 将子应用的输出变更上报主应用
1611
+ * - 包含事件类型、源/目标路径、相对路径等
1612
+ * - 主应用端在收到 CHANGE 后增量更新 app.json
1613
+ */
1383
1614
  state.fn = function () {
1384
1615
  return state.mfeClientServer.sendMessage(E_WS_TYPE.CHANGE, __assign(__assign({}, change), { appCode: manifestJson.value.appCode }));
1385
1616
  };
@@ -1397,6 +1628,10 @@ var createMainAppPlugin = function (manifestJson) {
1397
1628
  copyFilesByTargetPath(sourcePath, targetPath);
1398
1629
  };
1399
1630
  var watchFile = function () {
1631
+ /**
1632
+ * 在非 serve + 主应用构建时,监听所有子应用 Manifest 文件变化
1633
+ * - 变化时重算并写入 app.json,保持主应用 pages 配置最新
1634
+ */
1400
1635
  var allManifest = getAppsManifestList(manifestJson.value.mode);
1401
1636
  var watchFileList = allManifest.map(function (item) { return item.filePath; });
1402
1637
  var watcher = createFileWatcher(watchFileList);
@@ -1422,13 +1657,16 @@ var createMainAppPlugin = function (manifestJson) {
1422
1657
  start();
1423
1658
  return [2 /*return*/];
1424
1659
  }
1660
+ /**
1661
+ * 联调开发:启动根应用 WS 服务,负责给子应用下发初始化信息并接收变更事件
1662
+ */
1425
1663
  createMainAppServer();
1426
- // const subs = findLocalSubApps();
1427
- // if (subs.length) {
1428
- // startLocalSubApps(subs, "mp-weixin", manifestJson.value.mode);
1429
- // }
1430
1664
  }
1431
1665
  else {
1666
+ /**
1667
+ * 子应用:作为 WS 客户端连接主应用
1668
+ * - 收到 INIT 后,将自身产物拷贝到主应用分包路径
1669
+ */
1432
1670
  state.mfeClientServer = createMainAppClient(manifestJson, function (data) {
1433
1671
  copyAppDistModule(data);
1434
1672
  });
@@ -1498,8 +1736,237 @@ var createMainAppPlugin = function (manifestJson) {
1498
1736
  ];
1499
1737
  };
1500
1738
 
1739
+ /**
1740
+ * 运行时暴露插件(Exposes)
1741
+ * - 目标:在页面/模块中以 `@dd-code/runtime` 或 `@dd-code/runtime/<name>` 引用主应用的公共导出
1742
+ * - 流程:
1743
+ * 1) configResolved:从主应用 Manifest 读取 exposes 映射('.'、'./axios' 等)
1744
+ * 2) resolveId:拦截并识别 runtime 请求
1745
+ * 3) load:按请求的子路径拼装 require 代码,指向主应用模块(使用相对路径计算)
1746
+ * 4) buildStart:为每个 exposes 键发射独立的入口 chunk,确保输出到根目录
1747
+ * 5) generateBundle:收集产物映射(fileName + exports),保存到当前 Manifest
1748
+ * - 注意:
1749
+ * - `RUNTIME_NAME_REGEXP` 用于识别所有 runtime 形式,键以 '.' 开头并支持层级 './xxx'
1750
+ * - require 的相对路径通过 `renderRuntimeCode` 按当前 appCode + 构建位置计算,保持稳定
1751
+ */
1752
+ var buildName = "mfe_runtime/__mfe_{template}_runtime__.js";
1753
+ var RUNTIME_NAME = "@dd-code/runtime";
1754
+ // const runtimeDir = 'mfe_runtime'
1755
+ var RUNTIME_NAME_REGEXP = new RegExp(RUNTIME_NAME + "(/.*)?");
1756
+ /**
1757
+ * 生成 runtime 入口代码
1758
+ * - 参数:
1759
+ * - moduleExpose:主应用 exposes 配置中指定的模块信息({ path, exports })
1760
+ * - appCode:当前应用代码,用于计算运行时代码与目标模块的相对路径
1761
+ * - 相对路径计算:
1762
+ * - runtime 文件输出位置:mfe_runtime/__mfe_{template}_runtime__.js
1763
+ * - 运行时代码中 require 的相对路径需指向主应用根下的模块文件(如 store/index.js)
1764
+ * - deepPath = 相对路径(从 runtime 所在目录到项目根 "."),用于拼接 require('deepPath/<filePath>')
1765
+ * - 导出约定:
1766
+ * - 仅支持命名导出列表(exports: string[]),以 `export const {name} = require(...)` 形式暴露
1767
+ */
1768
+ var renderRuntimeCode = function (moduleExpose, appCode) {
1769
+ var deepPath = path.relative(path.join(appCode, path.dirname(buildName)), ".");
1770
+ var resultCode = "";
1771
+ var _a = moduleExpose || {}, _b = _a.exports, exportName = _b === void 0 ? [] : _b, filePath = _a.path;
1772
+ exportName.forEach(function (item) {
1773
+ resultCode += "export const {".concat(item, "} = require('").concat(deepPath, "/").concat(filePath, "');");
1774
+ });
1775
+ return resultCode;
1776
+ };
1777
+ var createExposesPlugin = function (options, currentManifestJson) {
1778
+ if (options === void 0) { options = {}; }
1779
+ options.exposes = options.exposes || {};
1780
+ var mainAppExposeCode = {};
1781
+ return [
1782
+ {
1783
+ name: "@chagee:uni-exposes",
1784
+ enforce: "post",
1785
+ /**
1786
+ * 读取主应用的 exposes 配置
1787
+ * - 来源:根应用构建时生成的 Manifest(含 exposes 映射)
1788
+ * - 示例:
1789
+ * { '.': { path: 'store/index.js', exports: ['userStore'] },
1790
+ * './axios': { path: 'axios/index.js', exports: ['axios'] } }
1791
+ */
1792
+ configResolved: function (config) {
1793
+ return __awaiter(this, void 0, void 0, function () {
1794
+ var manifestJson;
1795
+ return __generator(this, function (_a) {
1796
+ switch (_a.label) {
1797
+ case 0: return [4 /*yield*/, getRootMainManifestJson(currentManifestJson.value)];
1798
+ case 1:
1799
+ manifestJson = _a.sent();
1800
+ mainAppExposeCode = manifestJson.exposes || {};
1801
+ return [2 /*return*/];
1802
+ }
1803
+ });
1804
+ });
1805
+ },
1806
+ /**
1807
+ * 统一拦截 runtime 引用
1808
+ * - 支持 @dd-code/runtime 与 @dd-code/runtime/<name>
1809
+ * - 返回原始 id,交由 load 钩子生成具体代码
1810
+ */
1811
+ resolveId: function (id, importer) {
1812
+ return __awaiter(this, void 0, void 0, function () {
1813
+ return __generator(this, function (_a) {
1814
+ if (RUNTIME_NAME_REGEXP.test(id)) {
1815
+ return [2 /*return*/, id];
1816
+ }
1817
+ return [2 /*return*/];
1818
+ });
1819
+ });
1820
+ },
1821
+ /**
1822
+ * 生成运行时代码
1823
+ * - 通过解析 id 获取子路径('.' 或 './name')
1824
+ * - 按 exposes 映射找到对应模块与导出列表,拼装 require 代码
1825
+ */
1826
+ load: function (id) {
1827
+ return __awaiter(this, void 0, void 0, function () {
1828
+ var matched, deepPath, moduleExpose;
1829
+ return __generator(this, function (_a) {
1830
+ if (RUNTIME_NAME_REGEXP.test(id)) {
1831
+ matched = id.match(RUNTIME_NAME_REGEXP);
1832
+ deepPath = "." + ((matched === null || matched === void 0 ? void 0 : matched[1]) || "");
1833
+ moduleExpose = mainAppExposeCode[deepPath] || {};
1834
+ return [2 /*return*/, renderRuntimeCode(moduleExpose, currentManifestJson.value.appCode)];
1835
+ }
1836
+ return [2 /*return*/];
1837
+ });
1838
+ });
1839
+ },
1840
+ /**
1841
+ * 为每个 exposes 键发射一个 runtime chunk
1842
+ * - fileName 模板中 {template} 取键名,将非路径字符替换为可用形式(如 './axios' -> '_axios')
1843
+ * - 保证各入口在输出根目录下具有稳定文件名
1844
+ */
1845
+ buildStart: function () {
1846
+ return __awaiter(this, void 0, void 0, function () {
1847
+ var _i, _a, _b, spec, r, id, fileName, fileNameWithoutExt;
1848
+ var _this = this;
1849
+ return __generator(this, function (_c) {
1850
+ switch (_c.label) {
1851
+ case 0:
1852
+ // 显式发射 runtime chunk,确保它被打包到根目录
1853
+ if (!currentManifestJson.value.isRoot) {
1854
+ Object.keys(mainAppExposeCode).forEach(function (key) {
1855
+ var keyDir = key.replace(/\./g, "").replace(/\//g, "_");
1856
+ _this.emitFile({
1857
+ type: "chunk",
1858
+ id: path.join(RUNTIME_NAME, key),
1859
+ fileName: buildName.replace("{template}", keyDir),
1860
+ });
1861
+ });
1862
+ }
1863
+ _i = 0, _a = Object.entries(options.exposes || {});
1864
+ _c.label = 1;
1865
+ case 1:
1866
+ if (!(_i < _a.length)) return [3 /*break*/, 4];
1867
+ _b = _a[_i], _b[0], spec = _b[1];
1868
+ return [4 /*yield*/, this.resolve(spec)];
1869
+ case 2:
1870
+ r = _c.sent();
1871
+ if (r) {
1872
+ id = r === null || r === void 0 ? void 0 : r.id;
1873
+ fileName = path.relative(process.cwd() + "/src/", id);
1874
+ fileNameWithoutExt = getFilePathWithoutExt(fileName);
1875
+ this.emitFile({
1876
+ type: "chunk",
1877
+ id: r.id,
1878
+ name: fileNameWithoutExt,
1879
+ });
1880
+ }
1881
+ _c.label = 3;
1882
+ case 3:
1883
+ _i++;
1884
+ return [3 /*break*/, 1];
1885
+ case 4: return [2 /*return*/];
1886
+ }
1887
+ });
1888
+ });
1889
+ },
1890
+ /**
1891
+ * 产物阶段:收集 exposes 的打包路径与导出列表
1892
+ * - 通过 chunk.modules[moduleId.id] 反查模块所属 chunk
1893
+ * - 保存到当前 Manifest,以便下游消费
1894
+ */
1895
+ generateBundle: function (_, bundles) {
1896
+ return __awaiter(this, void 0, void 0, function () {
1897
+ var chunks, resolveIdsMap, _loop_1, this_1, _a, _b, _c, _i, i;
1898
+ return __generator(this, function (_d) {
1899
+ switch (_d.label) {
1900
+ case 0:
1901
+ chunks = Object.values(bundles).filter(function (b) { return b.type === "chunk"; });
1902
+ resolveIdsMap = {};
1903
+ _loop_1 = function (i) {
1904
+ var moduleId, owner;
1905
+ return __generator(this, function (_e) {
1906
+ switch (_e.label) {
1907
+ case 0: return [4 /*yield*/, this_1.resolve(options.exposes[i])];
1908
+ case 1:
1909
+ moduleId = _e.sent();
1910
+ if (moduleId) {
1911
+ owner = chunks.find(function (c) { return c.modules && c.modules[moduleId.id]; });
1912
+ if (owner) {
1913
+ resolveIdsMap[i] = {
1914
+ path: owner.fileName,
1915
+ exports: owner.exports,
1916
+ };
1917
+ }
1918
+ }
1919
+ return [2 /*return*/];
1920
+ }
1921
+ });
1922
+ };
1923
+ this_1 = this;
1924
+ _a = options.exposes;
1925
+ _b = [];
1926
+ for (_c in _a)
1927
+ _b.push(_c);
1928
+ _i = 0;
1929
+ _d.label = 1;
1930
+ case 1:
1931
+ if (!(_i < _b.length)) return [3 /*break*/, 4];
1932
+ _c = _b[_i];
1933
+ if (!(_c in _a)) return [3 /*break*/, 3];
1934
+ i = _c;
1935
+ return [5 /*yield**/, _loop_1(i)];
1936
+ case 2:
1937
+ _d.sent();
1938
+ _d.label = 3;
1939
+ case 3:
1940
+ _i++;
1941
+ return [3 /*break*/, 1];
1942
+ case 4:
1943
+ currentManifestJson.setExposes(resolveIdsMap);
1944
+ return [2 /*return*/];
1945
+ }
1946
+ });
1947
+ });
1948
+ },
1949
+ },
1950
+ ];
1951
+ };
1952
+
1953
+ /**
1954
+ * mp-weixin 平台插件聚合器
1955
+ * - 初始化并维护当前构建的 Manifest 管理器(state + IO)
1956
+ * - 根据模式预清理发布目录并重置 outDir(避免脏数据影响产物)
1957
+ * - 组合注册各业务插件:运行时暴露、资源搬运、Manifest 采集与主应用逻辑
1958
+ * - 插件间通过 `currentManifestJson` 共享上下文(环境、文件列表、依赖、exposes 映射)
1959
+ * - 插件注册顺序:
1960
+ * 1) pre: @dd-code:genre-params(设置环境、重置 outDir)
1961
+ * 2) post: exposes(生成 runtime 入口与 exposes 产物映射)
1962
+ * 3) pre: apps-assets(下载/搬运其它应用资源到主输出目录)
1963
+ * 4) default: manifest-plugin(采集 pages.json 与产物清单)
1964
+ * 5) post: main-app(serve/watch/merge 核心逻辑)
1965
+ */
1501
1966
  var createMpWeixinUniPlugin = function (options) {
1967
+ if (options === void 0) { options = {}; }
1502
1968
  var currentManifestJson = createManifestManager();
1969
+ options.exposes = options.exposes || {};
1503
1970
  return [
1504
1971
  {
1505
1972
  name: "@dd-code:genre-params",
@@ -1509,7 +1976,7 @@ var createMpWeixinUniPlugin = function (options) {
1509
1976
  return __generator(this, function (_a) {
1510
1977
  switch (_a.label) {
1511
1978
  case 0:
1512
- currentManifestJson.setEnv(config.mode);
1979
+ currentManifestJson.setEnv(config.mode || "dev");
1513
1980
  // 清空 PUBLISH_PATH 目录
1514
1981
  unlinkDeepDirOrFile(PUBLISH_PATH);
1515
1982
  return [4 /*yield*/, resetOutDir(currentManifestJson, config)];
@@ -1521,6 +1988,7 @@ var createMpWeixinUniPlugin = function (options) {
1521
1988
  });
1522
1989
  },
1523
1990
  },
1991
+ createExposesPlugin(options, currentManifestJson),
1524
1992
  createAppsAssetsPlugin(currentManifestJson),
1525
1993
  createManifestPlugin(currentManifestJson),
1526
1994
  createMainAppPlugin(currentManifestJson),
@@ -1541,7 +2009,7 @@ var createMpWeixinUniPlugin = function (options) {
1541
2009
  var index = (function (options) {
1542
2010
  var platform = getPlatform();
1543
2011
  return platform === EPlaform.MP_WEIXIN
1544
- ? createMpWeixinUniPlugin()
2012
+ ? createMpWeixinUniPlugin(options)
1545
2013
  : [];
1546
2014
  });
1547
2015